diff --git a/.editorconfig b/.editorconfig
index a5dfab07a50..1583c600aa5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -129,7 +129,7 @@ csharp_indent_braces = false
csharp_indent_switch_labels = true
# Space preferences
-csharp_space_after_cast = true
+csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index a397604185a..7a8129df1a6 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -17,10 +17,17 @@ Small fixes/refactors are exempt.
Any media may be used in SS14 progress reports, with clear credit given.
If you're unsure whether your PR will require media, ask a maintainer.
-
-Check the box below to confirm that you have in fact seen this (put an X in the brackets, like [X]):
-->
+## Requirements
+
+- [ ] I have read and I am following the [Pull Request Guidelines](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html). I understand that not doing so may get my pr closed at maintainer’s discretion
- [ ] I have added screenshots/videos to this PR showcasing its changes ingame, **or** this PR does not require an ingame showcase
## Breaking changes
@@ -34,7 +41,7 @@ Make players aware of new features and changes that could affect how they play t
-->
-
-
+
+
+
+
diff --git a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
index 6d0b2a184f4..320bb88a67e 100644
--- a/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
+++ b/Content.Client/Access/UI/AgentIDCardWindow.xaml.cs
@@ -8,6 +8,7 @@
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;
+using System.Linq;
namespace Content.Client.Access.UI
{
@@ -17,19 +18,19 @@ public sealed partial class AgentIDCardWindow : DefaultWindow
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
private readonly SpriteSystem _spriteSystem;
- private readonly AgentIDCardBoundUserInterface _bui;
private const int JobIconColumnCount = 10;
public event Action? OnNameChanged;
public event Action? OnJobChanged;
- public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
+ public event Action>? OnJobIconChanged;
+
+ public AgentIDCardWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_spriteSystem = _entitySystem.GetEntitySystem();
- _bui = bui;
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text);
@@ -38,17 +39,16 @@ public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
}
- public void SetAllowedIcons(HashSet> icons, string currentJobIconId)
+ public void SetAllowedIcons(string currentJobIconId)
{
IconGrid.DisposeAllChildren();
- var jobIconGroup = new ButtonGroup();
+ var jobIconButtonGroup = new ButtonGroup();
var i = 0;
- foreach (var jobIconId in icons)
+ var icons = _prototypeManager.EnumeratePrototypes().Where(icon => icon.AllowSelection).ToList();
+ icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
+ foreach (var jobIcon in icons)
{
- if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
- continue;
-
String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount;
if (modulo == 0)
@@ -62,12 +62,13 @@ public void SetAllowedIcons(HashSet> icons, string
Access = AccessLevel.Public,
StyleClasses = { styleBase },
MaxSize = new Vector2(42, 28),
- Group = jobIconGroup,
- Pressed = i == 0,
+ Group = jobIconButtonGroup,
+ Pressed = currentJobIconId == jobIcon.ID,
+ ToolTip = jobIcon.LocalizedJobName
};
// Generate buttons textures
- TextureRect jobIconTexture = new TextureRect
+ var jobIconTexture = new TextureRect
{
Texture = _spriteSystem.Frame0(jobIcon.Icon),
TextureScale = new Vector2(2.5f, 2.5f),
@@ -75,12 +76,9 @@ public void SetAllowedIcons(HashSet> icons, string
};
jobIconButton.AddChild(jobIconTexture);
- jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
+ jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID);
IconGrid.AddChild(jobIconButton);
- if (jobIconId.Equals(currentJobIconId))
- jobIconButton.Pressed = true;
-
i++;
}
}
diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs
index b9d554607cf..f83b2609017 100644
--- a/Content.Client/Actions/ActionsSystem.cs
+++ b/Content.Client/Actions/ActionsSystem.cs
@@ -49,6 +49,7 @@ public override void Initialize()
SubscribeLocalEvent(OnInstantHandleState);
SubscribeLocalEvent(OnEntityTargetHandleState);
SubscribeLocalEvent(OnWorldTargetHandleState);
+ SubscribeLocalEvent(OnEntityWorldTargetHandleState);
}
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
@@ -77,6 +78,18 @@ private void OnWorldTargetHandleState(EntityUid uid, WorldTargetActionComponent
BaseHandleState(uid, component, state);
}
+ private void OnEntityWorldTargetHandleState(EntityUid uid,
+ EntityWorldTargetActionComponent component,
+ ref ComponentHandleState args)
+ {
+ if (args.Current is not EntityWorldTargetActionComponentState state)
+ return;
+
+ component.Whitelist = state.Whitelist;
+ component.CanTargetSelf = state.CanTargetSelf;
+ BaseHandleState(uid, component, state);
+ }
+
private void BaseHandleState(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
{
// TODO ACTIONS use auto comp states
@@ -108,7 +121,7 @@ private void BaseHandleState(EntityUid uid, BaseActionComponent component, Ba
UpdateAction(uid, component);
}
- protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
+ public override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
{
if (!ResolveActionData(actionId, ref action))
return;
diff --git a/Content.Client/Actions/UI/ActionAlertTooltip.cs b/Content.Client/Actions/UI/ActionAlertTooltip.cs
index ddc498b6e91..f805f6643d2 100644
--- a/Content.Client/Actions/UI/ActionAlertTooltip.cs
+++ b/Content.Client/Actions/UI/ActionAlertTooltip.cs
@@ -1,4 +1,4 @@
-using Content.Client.Stylesheets;
+using Content.Client.Stylesheets;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -77,9 +77,12 @@ public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string?
MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
};
- requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
- requires +
- "[/color]"));
+
+ if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))
+ return;
+
+ requiresLabel.SetMessage(markup);
+
vbox.AddChild(requiresLabel);
}
}
@@ -97,8 +100,11 @@ protected override void FrameUpdate(FrameEventArgs args)
if (timeLeft > TimeSpan.Zero)
{
var duration = Cooldown.Value.End - Cooldown.Value.Start;
- _cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
- $"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
+
+ if (!FormattedMessage.TryFromMarkup($"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]", out var markup))
+ return;
+
+ _cooldownLabel.SetMessage(markup);
_cooldownLabel.Visible = true;
}
else
diff --git a/Content.Client/Administration/AdminNameOverlay.cs b/Content.Client/Administration/AdminNameOverlay.cs
index c21ba2e32ca..27b2a5dedb0 100644
--- a/Content.Client/Administration/AdminNameOverlay.cs
+++ b/Content.Client/Administration/AdminNameOverlay.cs
@@ -44,7 +44,7 @@ protected override void Draw(in OverlayDrawArgs args)
}
// if not on the same map, continue
- if (_entityManager.GetComponent(entity.Value).MapID != _eyeManager.CurrentMap)
+ if (_entityManager.GetComponent(entity.Value).MapID != args.MapId)
{
continue;
}
diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
index ddd66623bd4..dd8e3e22121 100644
--- a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
+++ b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
@@ -88,26 +88,51 @@ public BwoinkControl()
var ach = AHelpHelper.EnsurePanel(a.SessionId);
var bch = AHelpHelper.EnsurePanel(b.SessionId);
- // First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
- // status, not number of unread messages, so that more recent unread messages take priority.
+ // Pinned players first
+ if (a.IsPinned != b.IsPinned)
+ return a.IsPinned ? -1 : 1;
+
+ // First, sort by unread. Any chat with unread messages appears first.
var aUnread = ach.Unread > 0;
var bUnread = bch.Unread > 0;
if (aUnread != bUnread)
return aUnread ? -1 : 1;
+ // Sort by recent messages during the current round.
+ var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
+ var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
+ if (aRecent != bRecent)
+ return aRecent ? -1 : 1;
+
// Next, sort by connection status. Any disconnected players are grouped towards the end.
if (a.Connected != b.Connected)
return a.Connected ? -1 : 1;
- // Next, group by whether or not the players have participated in this round.
- // The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
- if (a.ActiveThisRound != b.ActiveThisRound)
- return a.ActiveThisRound ? -1 : 1;
+ // Sort connected players by New Player status, then by Antag status
+ if (a.Connected && b.Connected)
+ {
+ var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
+ var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
+
+ if (aNewPlayer != bNewPlayer)
+ return aNewPlayer ? -1 : 1;
+
+ if (a.Antag != b.Antag)
+ return a.Antag ? -1 : 1;
+ }
+
+ // Sort disconnected players by participation in the round
+ if (!a.Connected && !b.Connected)
+ {
+ if (a.ActiveThisRound != b.ActiveThisRound)
+ return a.ActiveThisRound ? -1 : 1;
+ }
// Finally, sort by the most recent message.
return bch.LastMessage.CompareTo(ach.LastMessage);
};
+
Bans.OnPressed += _ =>
{
if (_currentPlayer is not null)
@@ -253,7 +278,20 @@ private void SwitchToChannel(NetUserId? ch)
public void PopulateList()
{
+ // Maintain existing pin statuses
+ var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
+
ChannelSelector.PopulateList();
+
+ // Restore pin statuses
+ foreach (var player in ChannelSelector.PlayerInfo)
+ {
+ if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
+ {
+ player.IsPinned = pinnedPlayer.IsPinned;
+ }
+ }
+
UpdateButtons();
}
}
diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
index 30f9d24df1d..e8653843c74 100644
--- a/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
+++ b/Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
@@ -30,7 +30,11 @@ public BwoinkWindow()
}
};
- OnOpen += () => Bwoink.PopulateList();
+ OnOpen += () =>
+ {
+ Bwoink.ChannelSelector.StopFiltering();
+ Bwoink.PopulateList();
+ };
}
}
}
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
index 12522d552d7..c7fbf6c2dc0 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
@@ -4,154 +4,166 @@
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
+using Robust.Shared.Utility;
-namespace Content.Client.Administration.UI.CustomControls
+namespace Content.Client.Administration.UI.CustomControls;
+
+[GenerateTypedNameReferences]
+public sealed partial class PlayerListControl : BoxContainer
{
- [GenerateTypedNameReferences]
- public sealed partial class PlayerListControl : BoxContainer
- {
- private readonly AdminSystem _adminSystem;
+ private readonly AdminSystem _adminSystem;
- private List _playerList = new();
- private readonly List _sortedPlayerList = new();
+ private readonly IEntityManager _entManager;
+ private readonly IUserInterfaceManager _uiManager;
- public event Action? OnSelectionChanged;
- public IReadOnlyList PlayerInfo => _playerList;
+ private PlayerInfo? _selectedPlayer;
- public Func? OverrideText;
- public Comparison? Comparison;
+ private List _playerList = new();
+ private List _sortedPlayerList = new();
- private IEntityManager _entManager;
- private IUserInterfaceManager _uiManager;
+ public Comparison? Comparison;
+ public Func? OverrideText;
- private PlayerInfo? _selectedPlayer;
+ public PlayerListControl()
+ {
+ _entManager = IoCManager.Resolve();
+ _uiManager = IoCManager.Resolve();
+ _adminSystem = _entManager.System();
+ RobustXamlLoader.Load(this);
+ // Fill the Option data
+ PlayerListContainer.ItemPressed += PlayerListItemPressed;
+ PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
+ PlayerListContainer.GenerateItem += GenerateButton;
+ PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
+ PopulateList(_adminSystem.PlayerList);
+ FilterLineEdit.OnTextChanged += _ => FilterList();
+ _adminSystem.PlayerListChanged += PopulateList;
+ BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
+ }
- public PlayerListControl()
- {
- _entManager = IoCManager.Resolve();
- _uiManager = IoCManager.Resolve();
- _adminSystem = _entManager.System();
- RobustXamlLoader.Load(this);
- // Fill the Option data
- PlayerListContainer.ItemPressed += PlayerListItemPressed;
- PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
- PlayerListContainer.GenerateItem += GenerateButton;
- PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
- PopulateList(_adminSystem.PlayerList);
- FilterLineEdit.OnTextChanged += _ => FilterList();
- _adminSystem.PlayerListChanged += PopulateList;
- BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
- }
+ public IReadOnlyList PlayerInfo => _playerList;
- private void PlayerListNoItemSelected()
- {
- _selectedPlayer = null;
- OnSelectionChanged?.Invoke(null);
- }
+ public event Action? OnSelectionChanged;
- private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
- {
- if (args == null || data is not PlayerListData {Info: var selectedPlayer})
- return;
+ private void PlayerListNoItemSelected()
+ {
+ _selectedPlayer = null;
+ OnSelectionChanged?.Invoke(null);
+ }
- if (selectedPlayer == _selectedPlayer)
- return;
+ private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
+ {
+ if (args == null || data is not PlayerListData { Info: var selectedPlayer })
+ return;
- if (args.Event.Function != EngineKeyFunctions.UIClick)
- return;
+ if (selectedPlayer == _selectedPlayer)
+ return;
- OnSelectionChanged?.Invoke(selectedPlayer);
- _selectedPlayer = selectedPlayer;
+ if (args.Event.Function != EngineKeyFunctions.UIClick)
+ return;
- // update label text. Only required if there is some override (e.g. unread bwoink count).
- if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
- label.Text = GetText(selectedPlayer);
- }
+ OnSelectionChanged?.Invoke(selectedPlayer);
+ _selectedPlayer = selectedPlayer;
- private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
- {
- if (args == null || data is not PlayerListData { Info: var selectedPlayer })
- return;
+ // update label text. Only required if there is some override (e.g. unread bwoink count).
+ if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
+ label.Text = GetText(selectedPlayer);
+ }
- if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
- return;
+ private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
+ {
+ if (args == null || data is not PlayerListData { Info: var selectedPlayer })
+ return;
- _uiManager.GetUIController().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
- args.Handle();
- }
+ if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
+ return;
+
+ _uiManager.GetUIController().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
+ args.Handle();
+ }
+
+ public void StopFiltering()
+ {
+ FilterLineEdit.Text = string.Empty;
+ }
- public void StopFiltering()
+ private void FilterList()
+ {
+ _sortedPlayerList.Clear();
+ foreach (var info in _playerList)
{
- FilterLineEdit.Text = string.Empty;
+ var displayName = $"{info.CharacterName} ({info.Username})";
+ if (info.IdentityName != info.CharacterName)
+ displayName += $" [{info.IdentityName}]";
+ if (!string.IsNullOrEmpty(FilterLineEdit.Text)
+ && !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
+ continue;
+ _sortedPlayerList.Add(info);
}
- private void FilterList()
- {
- _sortedPlayerList.Clear();
- foreach (var info in _playerList)
- {
- var displayName = $"{info.CharacterName} ({info.Username})";
- if (info.IdentityName != info.CharacterName)
- displayName += $" [{info.IdentityName}]";
- if (!string.IsNullOrEmpty(FilterLineEdit.Text)
- && !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
- continue;
- _sortedPlayerList.Add(info);
- }
+ if (Comparison != null)
+ _sortedPlayerList.Sort((a, b) => Comparison(a, b));
- if (Comparison != null)
- _sortedPlayerList.Sort((a, b) => Comparison(a, b));
+ PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
+ if (_selectedPlayer != null)
+ PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
+ }
- PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
- if (_selectedPlayer != null)
- PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
- }
- public void PopulateList(IReadOnlyList? players = null)
- {
- players ??= _adminSystem.PlayerList;
+ public void PopulateList(IReadOnlyList? players = null)
+ {
+ // Maintain existing pin statuses
+ var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
- _playerList = players.ToList();
- if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
- _selectedPlayer = null;
+ players ??= _adminSystem.PlayerList;
- FilterList();
- }
+ _playerList = players.ToList();
- private string GetText(PlayerInfo info)
+ // Restore pin statuses
+ foreach (var player in _playerList)
{
- var text = $"{info.CharacterName} ({info.Username})";
- if (OverrideText != null)
- text = OverrideText.Invoke(info, text);
- return text;
+ if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
+ {
+ player.IsPinned = pinnedPlayer.IsPinned;
+ }
}
- private void GenerateButton(ListData data, ListContainerButton button)
- {
- if (data is not PlayerListData { Info: var info })
- return;
+ if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
+ _selectedPlayer = null;
- button.AddChild(new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- Children =
- {
- new Label
- {
- ClipText = true,
- Text = GetText(info)
- }
- }
- });
-
- button.AddStyleClass(ListContainer.StyleClassListContainerButton);
- }
+ FilterList();
+ }
+
+
+ private string GetText(PlayerInfo info)
+ {
+ var text = $"{info.CharacterName} ({info.Username})";
+ if (OverrideText != null)
+ text = OverrideText.Invoke(info, text);
+ return text;
}
- public record PlayerListData(PlayerInfo Info) : ListData;
+ private void GenerateButton(ListData data, ListContainerButton button)
+ {
+ if (data is not PlayerListData { Info: var info })
+ return;
+
+ var entry = new PlayerListEntry();
+ entry.Setup(info, OverrideText);
+ entry.OnPinStatusChanged += _ =>
+ {
+ FilterList();
+ };
+
+ button.AddChild(entry);
+ button.AddStyleClass(ListContainer.StyleClassListContainerButton);
+ }
}
+
+public record PlayerListData(PlayerInfo Info) : ListData;
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml
new file mode 100644
index 00000000000..af13ccc0e09
--- /dev/null
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs
new file mode 100644
index 00000000000..cd6a56ea71e
--- /dev/null
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs
@@ -0,0 +1,58 @@
+using Content.Client.Stylesheets;
+using Content.Shared.Administration;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Administration.UI.CustomControls;
+
+[GenerateTypedNameReferences]
+public sealed partial class PlayerListEntry : BoxContainer
+{
+ public PlayerListEntry()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public event Action? OnPinStatusChanged;
+
+ public void Setup(PlayerInfo info, Func? overrideText)
+ {
+ Update(info, overrideText);
+ PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
+ }
+
+ private Action HandlePinButtonPressed(PlayerInfo info)
+ {
+ return args =>
+ {
+ info.IsPinned = !info.IsPinned;
+ UpdatePinButtonTexture(info.IsPinned);
+ OnPinStatusChanged?.Invoke(info);
+ };
+ }
+
+ private void Update(PlayerInfo info, Func? overrideText)
+ {
+ PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
+ $"{info.CharacterName} ({info.Username})";
+
+ UpdatePinButtonTexture(info.IsPinned);
+ }
+
+ private void UpdatePinButtonTexture(bool isPinned)
+ {
+ if (isPinned)
+ {
+ PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
+ PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
+ }
+ else
+ {
+ PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
+ PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
+ }
+ }
+}
diff --git a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml
index f978138ca58..4a3c0ef7ace 100644
--- a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml
+++ b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml
@@ -1,10 +1,10 @@
-
+
-
+
-
+
diff --git a/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml b/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml
new file mode 100644
index 00000000000..8feec273b47
--- /dev/null
+++ b/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml.cs b/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml.cs
new file mode 100644
index 00000000000..824d9eb6c77
--- /dev/null
+++ b/Content.Client/Administration/UI/PlayerPanel/PlayerPanel.xaml.cs
@@ -0,0 +1,132 @@
+using Content.Client.Administration.Managers;
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Administration;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Network;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Administration.UI.PlayerPanel;
+
+[GenerateTypedNameReferences]
+public sealed partial class PlayerPanel : FancyWindow
+{
+ private readonly IClientAdminManager _adminManager;
+
+ public event Action? OnUsernameCopy;
+ public event Action? OnOpenNotes;
+ public event Action? OnOpenBans;
+ public event Action? OnAhelp;
+ public event Action? OnKick;
+ public event Action? OnOpenBanPanel;
+ public event Action? OnWhitelistToggle;
+ public event Action? OnFreezeAndMuteToggle;
+ public event Action? OnFreeze;
+ public event Action? OnLogs;
+ public event Action? OnDelete;
+ public event Action? OnRejuvenate;
+
+ public NetUserId? TargetPlayer;
+ public string? TargetUsername;
+ private bool _isWhitelisted;
+
+ public PlayerPanel(IClientAdminManager adminManager)
+ {
+ RobustXamlLoader.Load(this);
+ _adminManager = adminManager;
+
+ UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
+ BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
+ KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
+ NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
+ ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
+ AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
+ WhitelistToggle.OnPressed += _ =>
+ {
+ OnWhitelistToggle?.Invoke(TargetPlayer, _isWhitelisted);
+ SetWhitelisted(!_isWhitelisted);
+ };
+ FreezeButton.OnPressed += _ => OnFreeze?.Invoke();
+ FreezeAndMuteToggleButton.OnPressed += _ => OnFreezeAndMuteToggle?.Invoke();
+ LogsButton.OnPressed += _ => OnLogs?.Invoke();
+ DeleteButton.OnPressed += _ => OnDelete?.Invoke();
+ RejuvenateButton.OnPressed += _ => OnRejuvenate?.Invoke();
+ }
+
+ public void SetUsername(string player)
+ {
+ Title = Loc.GetString("player-panel-title", ("player", player));
+ PlayerName.Text = Loc.GetString("player-panel-username", ("player", player));
+ }
+
+ public void SetWhitelisted(bool? whitelisted)
+ {
+ if (whitelisted == null)
+ {
+ Whitelisted.Text = null;
+ WhitelistToggle.Visible = false;
+ }
+ else
+ {
+ Whitelisted.Text = Loc.GetString("player-panel-whitelisted");
+ WhitelistToggle.Text = whitelisted.Value.ToString();
+ WhitelistToggle.Visible = true;
+ _isWhitelisted = whitelisted.Value;
+ }
+ }
+
+ public void SetBans(int? totalBans, int? totalRoleBans)
+ {
+ // If one value exists then so should the other.
+ DebugTools.Assert(totalBans.HasValue && totalRoleBans.HasValue || totalBans == null && totalRoleBans == null);
+
+ Bans.Text = totalBans != null ? Loc.GetString("player-panel-bans", ("totalBans", totalBans)) : null;
+
+ RoleBans.Text = totalRoleBans != null ? Loc.GetString("player-panel-rolebans", ("totalRoleBans", totalRoleBans)) : null;
+ }
+
+ public void SetNotes(int? totalNotes)
+ {
+ Notes.Text = totalNotes != null ? Loc.GetString("player-panel-notes", ("totalNotes", totalNotes)) : null;
+ }
+
+ public void SetSharedConnections(int sharedConnections)
+ {
+ SharedConnections.Text = Loc.GetString("player-panel-shared-connections", ("sharedConnections", sharedConnections));
+ }
+
+ public void SetPlaytime(TimeSpan playtime)
+ {
+ Playtime.Text = Loc.GetString("player-panel-playtime",
+ ("days", playtime.Days),
+ ("hours", playtime.Hours % 24),
+ ("minutes", playtime.Minutes % (24 * 60)));
+ }
+
+ public void SetFrozen(bool canFreeze, bool frozen)
+ {
+ FreezeAndMuteToggleButton.Disabled = !canFreeze;
+ FreezeButton.Disabled = !canFreeze || frozen;
+
+ FreezeAndMuteToggleButton.Text = Loc.GetString(!frozen ? "player-panel-freeze-and-mute" : "player-panel-unfreeze");
+ }
+
+ public void SetAhelp(bool canAhelp)
+ {
+ AhelpButton.Disabled = !canAhelp;
+ }
+
+ public void SetButtons()
+ {
+ BanButton.Disabled = !_adminManager.CanCommand("banpanel");
+ KickButton.Disabled = !_adminManager.CanCommand("kick");
+ NotesButton.Disabled = !_adminManager.CanCommand("adminnotes");
+ ShowBansButton.Disabled = !_adminManager.CanCommand("banlist");
+ WhitelistToggle.Disabled =
+ !(_adminManager.CanCommand("addwhitelist") && _adminManager.CanCommand("removewhitelist"));
+ LogsButton.Disabled = !_adminManager.CanCommand("adminlogs");
+ RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
+ DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
+ }
+}
diff --git a/Content.Client/Administration/UI/PlayerPanel/PlayerPanelEui.cs b/Content.Client/Administration/UI/PlayerPanel/PlayerPanelEui.cs
new file mode 100644
index 00000000000..87ce7560463
--- /dev/null
+++ b/Content.Client/Administration/UI/PlayerPanel/PlayerPanelEui.cs
@@ -0,0 +1,72 @@
+using Content.Client.Administration.Managers;
+using Content.Client.Eui;
+using Content.Shared.Administration;
+using Content.Shared.Eui;
+using JetBrains.Annotations;
+using Robust.Client.Console;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Administration.UI.PlayerPanel;
+
+[UsedImplicitly]
+public sealed class PlayerPanelEui : BaseEui
+{
+ [Dependency] private readonly IClientConsoleHost _console = default!;
+ [Dependency] private readonly IClientAdminManager _admin = default!;
+ [Dependency] private readonly IClipboardManager _clipboard = default!;
+
+ private PlayerPanel PlayerPanel { get; }
+
+ public PlayerPanelEui()
+ {
+ PlayerPanel = new PlayerPanel(_admin);
+
+ PlayerPanel.OnUsernameCopy += username => _clipboard.SetText(username);
+ PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
+ // Kick command does not support GUIDs
+ PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
+ PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
+ PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
+ PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
+ PlayerPanel.OnWhitelistToggle += (id, whitelisted) =>
+ {
+ _console.ExecuteCommand(whitelisted ? $"whitelistremove \"{id}\"" : $"whitelistadd \"{id}\"");
+ };
+
+ PlayerPanel.OnFreezeAndMuteToggle += () => SendMessage(new PlayerPanelFreezeMessage(true));
+ PlayerPanel.OnFreeze += () => SendMessage(new PlayerPanelFreezeMessage());
+ PlayerPanel.OnLogs += () => SendMessage(new PlayerPanelLogsMessage());
+ PlayerPanel.OnRejuvenate += () => SendMessage(new PlayerPanelRejuvenationMessage());
+ PlayerPanel.OnDelete+= () => SendMessage(new PlayerPanelDeleteMessage());
+
+ PlayerPanel.OnClose += () => SendMessage(new CloseEuiMessage());
+ }
+
+ public override void Opened()
+ {
+ PlayerPanel.OpenCentered();
+ }
+
+ public override void Closed()
+ {
+ PlayerPanel.Close();
+ }
+
+ public override void HandleState(EuiStateBase state)
+ {
+ if (state is not PlayerPanelEuiState s)
+ return;
+
+ PlayerPanel.TargetPlayer = s.Guid;
+ PlayerPanel.TargetUsername = s.Username;
+ PlayerPanel.SetUsername(s.Username);
+ PlayerPanel.SetPlaytime(s.Playtime);
+ PlayerPanel.SetBans(s.TotalBans, s.TotalRoleBans);
+ PlayerPanel.SetNotes(s.TotalNotes);
+ PlayerPanel.SetWhitelisted(s.Whitelisted);
+ PlayerPanel.SetSharedConnections(s.SharedConnections);
+ PlayerPanel.SetFrozen(s.CanFreeze, s.Frozen);
+ PlayerPanel.SetAhelp(s.CanAhelp);
+ PlayerPanel.SetButtons();
+ }
+}
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
index 78eefa34628..4b50771b0fe 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
@@ -1,20 +1,21 @@
+using Content.Client.Administration.Managers;
using Content.Client.Station;
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
+using Robust.Client.Console;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map.Components;
-using Robust.Shared.Timing;
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
[GenerateTypedNameReferences]
public sealed partial class ObjectsTab : Control
{
+ [Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
- [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IClientConsoleHost _console = default!;
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
@@ -50,10 +51,20 @@ public ObjectsTab()
RefreshListButton.OnPressed += _ => RefreshObjectList();
var defaultSelection = ObjectsTabSelection.Grids;
- ObjectTypeOptions.SelectId((int) defaultSelection);
+ ObjectTypeOptions.SelectId((int)defaultSelection);
RefreshObjectList(defaultSelection);
}
+ private void TeleportTo(NetEntity nent)
+ {
+ _console.ExecuteCommand($"tpto {nent}");
+ }
+
+ private void Delete(NetEntity nent)
+ {
+ _console.ExecuteCommand($"delete {nent}");
+ }
+
public void RefreshObjectList()
{
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
@@ -117,9 +128,9 @@ private void GenerateButton(ListData data, ListContainerButton button)
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
return;
- var entry = new ObjectsTabEntry(info.Name,
- info.Entity,
- new StyleBoxFlat { BackgroundColor = backgroundColor });
+ var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
+ entry.OnTeleport += TeleportTo;
+ entry.OnDelete += Delete;
button.ToolTip = $"{info.Name}, {info.Entity}";
button.AddChild(entry);
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
index 83c4cc5697f..c561125a30c 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
@@ -5,13 +5,25 @@
HorizontalExpand="True"
SeparationOverride="4">
+
+
+
+
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
index aab06c6ccd0..29774bb587e 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
@@ -1,4 +1,5 @@
-using Robust.Client.AutoGenerated;
+using Content.Client.Administration.Managers;
+using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
@@ -10,12 +11,22 @@ public sealed partial class ObjectsTabEntry : PanelContainer
{
public NetEntity AssocEntity;
- public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
+ public Action? OnTeleport;
+ public Action? OnDelete;
+
+ public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
{
RobustXamlLoader.Load(this);
+
AssocEntity = nent;
EIDLabel.Text = nent.ToString();
NameLabel.Text = name;
BackgroundColorPanel.PanelOverride = styleBox;
+
+ TeleportButton.Disabled = !manager.CanCommand("tpto");
+ DeleteButton.Disabled = !manager.CanCommand("delete");
+
+ TeleportButton.OnPressed += _ => OnTeleport?.Invoke(nent);
+ DeleteButton.OnPressed += _ => OnDelete?.Invoke(nent);
}
}
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml
index 71a1f5c7bc6..2cc7d76180e 100644
--- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml
+++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml
@@ -5,17 +5,23 @@
HorizontalExpand="True"
SeparationOverride="4">
+
+
diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs
index 359c8957f9d..525ef1f018f 100644
--- a/Content.Client/Alerts/ClientAlertsSystem.cs
+++ b/Content.Client/Alerts/ClientAlertsSystem.cs
@@ -93,6 +93,6 @@ private void OnPlayerDetached(EntityUid uid, AlertsComponent component, LocalPla
public void AlertClicked(ProtoId alertType)
{
- RaiseNetworkEvent(new ClickAlertEvent(alertType));
+ RaisePredictiveEvent(new ClickAlertEvent(alertType));
}
}
diff --git a/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs b/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs
index e84cf5d34de..3d65f751899 100644
--- a/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs
+++ b/Content.Client/Ame/UI/AmeControllerBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Ame.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Ame.UI
{
@@ -16,9 +17,8 @@ protected override void Open()
{
base.Open();
- _window = new AmeWindow(this);
- _window.OnClose += Close;
- _window.OpenCentered();
+ _window = this.CreateWindow();
+ _window.OnAmeButton += ButtonPressed;
}
///
@@ -40,15 +40,5 @@ public void ButtonPressed(UiButton button)
{
SendMessage(new UiButtonPressedMessage(button));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
}
}
diff --git a/Content.Client/Ame/UI/AmeWindow.xaml.cs b/Content.Client/Ame/UI/AmeWindow.xaml.cs
index 8b91ec59660..d6d580bcdaf 100644
--- a/Content.Client/Ame/UI/AmeWindow.xaml.cs
+++ b/Content.Client/Ame/UI/AmeWindow.xaml.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using Content.Client.UserInterface;
using Content.Shared.Ame.Components;
using Robust.Client.AutoGenerated;
@@ -9,15 +10,17 @@ namespace Content.Client.Ame.UI
[GenerateTypedNameReferences]
public sealed partial class AmeWindow : DefaultWindow
{
- public AmeWindow(AmeControllerBoundUserInterface ui)
+ public event Action? OnAmeButton;
+
+ public AmeWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject);
- ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection);
- IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel);
- DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel);
+ EjectButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.Eject);
+ ToggleInjection.OnPressed += _ => OnAmeButton?.Invoke(UiButton.ToggleInjection);
+ IncreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.IncreaseFuel);
+ DecreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.DecreaseFuel);
}
///
@@ -29,7 +32,7 @@ public void UpdateState(BoundUserInterfaceState state)
var castState = (AmeControllerBoundUserInterfaceState) state;
// Disable all buttons if not powered
- if (Contents.Children != null)
+ if (Contents.Children.Any())
{
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
EjectButton.Disabled = false;
@@ -65,8 +68,8 @@ public void UpdateState(BoundUserInterfaceState state)
CoreCount.Text = $"{castState.CoreCount}";
InjectionAmount.Text = $"{castState.InjectionAmount}";
// format power statistics to pretty numbers
- CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply.ToString("N1")}";
- TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply.ToString("N1")}";
+ CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply:N1}";
+ TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply:N1}";
}
}
}
diff --git a/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs b/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs
index 5764d0a097d..5d1985485c4 100644
--- a/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs
+++ b/Content.Client/Anomaly/Ui/AnomalyGeneratorBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Gravity;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Anomaly.Ui;
@@ -18,10 +19,8 @@ protected override void Open()
{
base.Open();
- _window = new(Owner);
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.SetEntity(Owner);
_window.OnGenerateButtonPressed += () =>
{
@@ -37,18 +36,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
_window?.UpdateState(msg);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
-
- _window?.Dispose();
- }
-
- public void SetPowerSwitch(bool on)
- {
- SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on));
- }
}
diff --git a/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs b/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs
index 08438e2a1b2..82d41192dd0 100644
--- a/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs
+++ b/Content.Client/Anomaly/Ui/AnomalyGeneratorWindow.xaml.cs
@@ -18,17 +18,21 @@ public sealed partial class AnomalyGeneratorWindow : FancyWindow
public Action? OnGenerateButtonPressed;
- public AnomalyGeneratorWindow(EntityUid gen)
+ public AnomalyGeneratorWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- EntityView.SetEntity(gen);
EntityView.SpriteOffset = false;
GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke();
}
+ public void SetEntity(EntityUid uid)
+ {
+ EntityView.SetEntity(uid);
+ }
+
public void UpdateState(AnomalyGeneratorUserInterfaceState state)
{
_cooldownEnd = state.CooldownEndTime;
diff --git a/Content.Client/Arcade/BlockGameMenu.cs b/Content.Client/Arcade/BlockGameMenu.cs
index eeda2a31020..ad360c54394 100644
--- a/Content.Client/Arcade/BlockGameMenu.cs
+++ b/Content.Client/Arcade/BlockGameMenu.cs
@@ -28,8 +28,6 @@ public sealed class BlockGameMenu : DefaultWindow
private static readonly Vector2 BlockSize = new(15, 15);
- private readonly BlockGameBoundUserInterface _owner;
-
private readonly PanelContainer _mainPanel;
private readonly BoxContainer _gameRootContainer;
@@ -58,10 +56,11 @@ public sealed class BlockGameMenu : DefaultWindow
private bool _isPlayer = false;
private bool _gameOver = false;
- public BlockGameMenu(BlockGameBoundUserInterface owner)
+ public event Action? OnAction;
+
+ public BlockGameMenu()
{
Title = Loc.GetString("blockgame-menu-title");
- _owner = owner;
MinSize = SetSize = new Vector2(410, 490);
@@ -176,7 +175,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner)
};
_newGameButton.OnPressed += (e) =>
{
- _owner.SendAction(BlockGamePlayerAction.NewGame);
+ OnAction?.Invoke(BlockGamePlayerAction.NewGame);
};
pauseMenuContainer.AddChild(_newGameButton);
pauseMenuContainer.AddChild(new Control { MinSize = new Vector2(1, 10) });
@@ -186,7 +185,10 @@ public BlockGameMenu(BlockGameBoundUserInterface owner)
Text = Loc.GetString("blockgame-menu-button-scoreboard"),
TextAlign = Label.AlignMode.Center
};
- _scoreBoardButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.ShowHighscores);
+ _scoreBoardButton.OnPressed += (e) =>
+ {
+ OnAction?.Invoke(BlockGamePlayerAction.ShowHighscores);
+ };
pauseMenuContainer.AddChild(_scoreBoardButton);
_unpauseButtonMargin = new Control { MinSize = new Vector2(1, 10), Visible = false };
pauseMenuContainer.AddChild(_unpauseButtonMargin);
@@ -199,7 +201,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner)
};
_unpauseButton.OnPressed += (e) =>
{
- _owner.SendAction(BlockGamePlayerAction.Unpause);
+ OnAction?.Invoke(BlockGamePlayerAction.Unpause);
};
pauseMenuContainer.AddChild(_unpauseButton);
@@ -257,7 +259,7 @@ public BlockGameMenu(BlockGameBoundUserInterface owner)
};
_finalNewGameButton.OnPressed += (e) =>
{
- _owner.SendAction(BlockGamePlayerAction.NewGame);
+ OnAction?.Invoke(BlockGamePlayerAction.NewGame);
};
gameOverMenuContainer.AddChild(_finalNewGameButton);
@@ -327,7 +329,10 @@ public BlockGameMenu(BlockGameBoundUserInterface owner)
Text = Loc.GetString("blockgame-menu-button-back"),
TextAlign = Label.AlignMode.Center
};
- _highscoreBackButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.Pause);
+ _highscoreBackButton.OnPressed += (e) =>
+ {
+ OnAction?.Invoke(BlockGamePlayerAction.Pause);
+ };
menuContainer.AddChild(_highscoreBackButton);
menuInnerPanel.AddChild(menuContainer);
@@ -375,7 +380,7 @@ private Control SetupGameGrid(Texture panelTex)
{
PanelOverride = back,
HorizontalExpand = true,
- SizeFlagsStretchRatio = 60
+ SizeFlagsStretchRatio = 34.25f
};
var backgroundPanel = new PanelContainer
{
@@ -473,7 +478,7 @@ protected override void KeyboardFocusExited()
private void TryPause()
{
- _owner.SendAction(BlockGamePlayerAction.Pause);
+ OnAction?.Invoke(BlockGamePlayerAction.Pause);
}
public void SetStarted()
@@ -576,19 +581,19 @@ protected override void KeyBindDown(GUIBoundKeyEventArgs args)
return;
else if (args.Function == ContentKeyFunctions.ArcadeLeft)
- _owner.SendAction(BlockGamePlayerAction.StartLeft);
+ OnAction?.Invoke(BlockGamePlayerAction.StartLeft);
else if (args.Function == ContentKeyFunctions.ArcadeRight)
- _owner.SendAction(BlockGamePlayerAction.StartRight);
+ OnAction?.Invoke(BlockGamePlayerAction.StartRight);
else if (args.Function == ContentKeyFunctions.ArcadeUp)
- _owner.SendAction(BlockGamePlayerAction.Rotate);
+ OnAction?.Invoke(BlockGamePlayerAction.Rotate);
else if (args.Function == ContentKeyFunctions.Arcade3)
- _owner.SendAction(BlockGamePlayerAction.CounterRotate);
+ OnAction?.Invoke(BlockGamePlayerAction.CounterRotate);
else if (args.Function == ContentKeyFunctions.ArcadeDown)
- _owner.SendAction(BlockGamePlayerAction.SoftdropStart);
+ OnAction?.Invoke(BlockGamePlayerAction.SoftdropStart);
else if (args.Function == ContentKeyFunctions.Arcade2)
- _owner.SendAction(BlockGamePlayerAction.Hold);
+ OnAction?.Invoke(BlockGamePlayerAction.Hold);
else if (args.Function == ContentKeyFunctions.Arcade1)
- _owner.SendAction(BlockGamePlayerAction.Harddrop);
+ OnAction?.Invoke(BlockGamePlayerAction.Harddrop);
}
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
@@ -599,11 +604,11 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args)
return;
else if (args.Function == ContentKeyFunctions.ArcadeLeft)
- _owner.SendAction(BlockGamePlayerAction.EndLeft);
+ OnAction?.Invoke(BlockGamePlayerAction.EndLeft);
else if (args.Function == ContentKeyFunctions.ArcadeRight)
- _owner.SendAction(BlockGamePlayerAction.EndRight);
+ OnAction?.Invoke(BlockGamePlayerAction.EndRight);
else if (args.Function == ContentKeyFunctions.ArcadeDown)
- _owner.SendAction(BlockGamePlayerAction.SoftdropEnd);
+ OnAction?.Invoke(BlockGamePlayerAction.SoftdropEnd);
}
public void UpdateNextBlock(BlockGameBlock[] blocks)
diff --git a/Content.Client/Arcade/SpaceVillainArcadeMenu.cs b/Content.Client/Arcade/SpaceVillainArcadeMenu.cs
index e5542a5848e..1ee4c268184 100644
--- a/Content.Client/Arcade/SpaceVillainArcadeMenu.cs
+++ b/Content.Client/Arcade/SpaceVillainArcadeMenu.cs
@@ -8,8 +8,6 @@ namespace Content.Client.Arcade
{
public sealed class SpaceVillainArcadeMenu : DefaultWindow
{
- public SpaceVillainArcadeBoundUserInterface Owner { get; set; }
-
private readonly Label _enemyNameLabel;
private readonly Label _playerInfoLabel;
private readonly Label _enemyInfoLabel;
@@ -17,11 +15,13 @@ public sealed class SpaceVillainArcadeMenu : DefaultWindow
private readonly Label _enemyActionLabel;
private readonly Button[] _gameButtons = new Button[3]; //used to disable/enable all game buttons
- public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner)
+
+ public event Action? OnPlayerAction;
+
+ public SpaceVillainArcadeMenu()
{
MinSize = SetSize = new Vector2(300, 225);
Title = Loc.GetString("spacevillain-menu-title");
- Owner = owner;
var grid = new GridContainer { Columns = 1 };
@@ -47,32 +47,43 @@ public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner)
grid.AddChild(_enemyActionLabel);
var buttonGrid = new GridContainer { Columns = 3 };
- _gameButtons[0] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Attack)
+ _gameButtons[0] = new Button()
{
Text = Loc.GetString("spacevillain-menu-button-attack")
};
+
+ _gameButtons[0].OnPressed +=
+ _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Attack);
buttonGrid.AddChild(_gameButtons[0]);
- _gameButtons[1] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Heal)
+ _gameButtons[1] = new Button()
{
Text = Loc.GetString("spacevillain-menu-button-heal")
};
+
+ _gameButtons[1].OnPressed +=
+ _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Heal);
buttonGrid.AddChild(_gameButtons[1]);
- _gameButtons[2] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Recharge)
+ _gameButtons[2] = new Button()
{
Text = Loc.GetString("spacevillain-menu-button-recharge")
};
+
+ _gameButtons[2].OnPressed +=
+ _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Recharge);
buttonGrid.AddChild(_gameButtons[2]);
centerContainer = new CenterContainer();
centerContainer.AddChild(buttonGrid);
grid.AddChild(centerContainer);
- var newGame = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.NewGame)
+ var newGame = new Button()
{
Text = Loc.GetString("spacevillain-menu-button-new-game")
};
+
+ newGame.OnPressed += _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.NewGame);
grid.AddChild(newGame);
Contents.AddChild(grid);
@@ -99,23 +110,5 @@ public void UpdateInfo(SharedSpaceVillainArcadeComponent.SpaceVillainArcadeDataU
_playerActionLabel.Text = message.PlayerActionMessage;
_enemyActionLabel.Text = message.EnemyActionMessage;
}
-
- private sealed class ActionButton : Button
- {
- private readonly SpaceVillainArcadeBoundUserInterface _owner;
- private readonly SharedSpaceVillainArcadeComponent.PlayerAction _playerAction;
-
- public ActionButton(SpaceVillainArcadeBoundUserInterface owner, SharedSpaceVillainArcadeComponent.PlayerAction playerAction)
- {
- _owner = owner;
- _playerAction = playerAction;
- OnPressed += Clicked;
- }
-
- private void Clicked(ButtonEventArgs e)
- {
- _owner.SendAction(_playerAction);
- }
- }
}
}
diff --git a/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs b/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs
index 1a3422dec0f..4f08e6bd0ac 100644
--- a/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs
+++ b/Content.Client/Arcade/UI/BlockGameBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Arcade;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Arcade.UI;
@@ -15,9 +16,8 @@ protected override void Open()
{
base.Open();
- _menu = new BlockGameMenu(this);
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnAction += SendAction;
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
diff --git a/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs b/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs
index 40bbe8b2d8c..8fff406e86c 100644
--- a/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs
+++ b/Content.Client/Arcade/UI/SpaceVillainArcadeBoundUserInterface.cs
@@ -1,4 +1,5 @@
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
@@ -9,8 +10,6 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
{
[ViewVariables] private SpaceVillainArcadeMenu? _menu;
- //public SharedSpaceVillainArcadeComponent SpaceVillainArcade;
-
public SpaceVillainArcadeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
SendAction(PlayerAction.RequestData);
@@ -25,10 +24,8 @@ protected override void Open()
{
base.Open();
- _menu = new SpaceVillainArcadeMenu(this);
-
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnPlayerAction += SendAction;
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
@@ -36,12 +33,4 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
if (message is SpaceVillainArcadeDataUpdateMessage msg)
_menu?.UpdateInfo(msg);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Atmos/EntitySystems/GasMinerSystem.cs b/Content.Client/Atmos/EntitySystems/GasMinerSystem.cs
new file mode 100644
index 00000000000..aec138eed8f
--- /dev/null
+++ b/Content.Client/Atmos/EntitySystems/GasMinerSystem.cs
@@ -0,0 +1,10 @@
+using Content.Shared.Atmos.EntitySystems;
+using JetBrains.Annotations;
+
+namespace Content.Client.Atmos.EntitySystems;
+
+[UsedImplicitly]
+public sealed class GasMinerSystem : SharedGasMinerSystem
+{
+
+}
diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
index 8f3b507c806..2ae15188355 100644
--- a/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
+++ b/Content.Client/Atmos/Monitor/UI/AirAlarmBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -20,16 +21,9 @@ protected override void Open()
{
base.Open();
- _window = new AirAlarmWindow(this);
+ _window = this.CreateWindow();
+ _window.SetEntity(Owner);
- if (State != null)
- {
- UpdateState(State);
- }
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
_window.AtmosDeviceDataChanged += OnDeviceDataChanged;
_window.AtmosDeviceDataCopied += OnDeviceDataCopied;
_window.AtmosAlarmThresholdChanged += OnThresholdChanged;
diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
index 43be67c9d6b..eeec11c7660 100644
--- a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
+++ b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs
@@ -47,7 +47,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
private CheckBox _autoMode => AutoModeCheckBox;
- public AirAlarmWindow(BoundUserInterface owner)
+ public AirAlarmWindow()
{
RobustXamlLoader.Load(this);
@@ -95,8 +95,11 @@ public AirAlarmWindow(BoundUserInterface owner)
_sensors.Clear();
ResyncAllRequested!.Invoke();
};
+ }
- EntityView.SetEntity(owner.Owner);
+ public void SetEntity(EntityUid uid)
+ {
+ EntityView.SetEntity(uid);
}
public void UpdateState(AirAlarmUIState state)
diff --git a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs
index a5e316a8def..7bf9b396d5e 100644
--- a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Atmos.Piping.Binary.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -21,14 +22,8 @@ protected override void Open()
{
base.Open();
- _window = new GasCanisterWindow();
+ _window = this.CreateWindow();
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
_window.ReleaseValveCloseButtonPressed += OnReleaseValveClosePressed;
_window.ReleaseValveOpenButtonPressed += OnReleaseValveOpenPressed;
_window.ReleasePressureSet += OnReleasePressureSet;
diff --git a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
index 1904e2b3402..2b8020924cf 100644
--- a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -28,14 +29,8 @@ protected override void Open()
var atmosSystem = EntMan.System();
- _window = new GasFilterWindow(atmosSystem.Gases);
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.PopulateGasList(atmosSystem.Gases);
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.FilterTransferRateChanged += OnFilterTransferRatePressed;
diff --git a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
index 28766c688a0..62748b52592 100644
--- a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
+++ b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
@@ -26,10 +26,9 @@ public sealed partial class GasFilterWindow : DefaultWindow
public event Action? FilterTransferRateChanged;
public event Action? SelectGasPressed;
- public GasFilterWindow(IEnumerable gases)
+ public GasFilterWindow()
{
RobustXamlLoader.Load(this);
- PopulateGasList(gases);
ToggleStatusButton.OnPressed += _ => SetFilterStatus(!FilterStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
@@ -73,7 +72,7 @@ public void SetGasFiltered(string? id, string name)
SelectGasButton.Disabled = true;
}
- private void PopulateGasList(IEnumerable gases)
+ public void PopulateGasList(IEnumerable gases)
{
GasList.Add(new ItemList.Item(GasList)
{
@@ -81,7 +80,7 @@ private void PopulateGasList(IEnumerable gases)
Text = Loc.GetString("comp-gas-filter-ui-filter-gas-none")
});
- foreach (GasPrototype gas in gases)
+ foreach (var gas in gases)
{
var gasName = Loc.GetString(gas.Name);
GasList.Add(GetGasItem(gas.ID, gasName, GasList));
diff --git a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
index 709c06517cb..392fbf1cd9a 100644
--- a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
+++ b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
@@ -2,7 +2,7 @@
using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -26,14 +26,7 @@ protected override void Open()
{
base.Open();
- _window = new GasMixerWindow();
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.MixerOutputPressureChanged += OnMixerOutputPressurePressed;
@@ -83,12 +76,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.SetOutputPressure(cast.OutputPressure);
_window.SetNodePercentages(cast.NodeOne);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
index 6eba2e0d215..220fdbe875c 100644
--- a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -26,14 +27,7 @@ protected override void Open()
{
base.Open();
- _window = new GasPressurePumpWindow();
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed;
@@ -67,12 +61,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.SetPumpStatus(cast.Enabled);
_window.SetOutputPressure(cast.OutputPressure);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs b/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs
index 1664c8b9d75..d62be8f4bb4 100644
--- a/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasThermomachineBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Atmos.Piping.Unary.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -31,14 +32,7 @@ protected override void Open()
{
base.Open();
- _window = new GasThermomachineWindow();
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value);
@@ -91,12 +85,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
true => Loc.GetString("comp-gas-thermomachine-ui-title-heater")
};
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
index 1b39306181a..642f34c2f92 100644
--- a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI
{
@@ -26,14 +27,7 @@ protected override void Open()
{
base.Open();
- _window = new GasVolumePumpWindow();
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.PumpTransferRateChanged += OnPumpTransferRatePressed;
@@ -64,16 +58,9 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (_window == null || state is not GasVolumePumpBoundUserInterfaceState cast)
return;
- _window.Title = (cast.PumpLabel);
+ _window.Title = cast.PumpLabel;
_window.SetPumpStatus(cast.Enabled);
_window.SetTransferRate(cast.TransferRate);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs b/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs
index 4d8d1191e91..e70426575d4 100644
--- a/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/SpaceHeaterBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Atmos.Piping.Portable.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Atmos.UI;
@@ -21,14 +22,7 @@ protected override void Open()
{
base.Open();
- _window = new SpaceHeaterWindow();
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta);
diff --git a/Content.Client/Audio/AudioUIController.cs b/Content.Client/Audio/AudioUIController.cs
index ef903672fd0..16e1edd2523 100644
--- a/Content.Client/Audio/AudioUIController.cs
+++ b/Content.Client/Audio/AudioUIController.cs
@@ -54,7 +54,7 @@ private void SetClickSound(string value)
{
if (!string.IsNullOrEmpty(value))
{
- var resource = _cache.GetResource(value);
+ var resource = GetSoundOrFallback(value, CCVars.UIClickSound.DefaultValue);
var source =
_audioManager.CreateAudioSource(resource);
@@ -77,7 +77,7 @@ private void SetHoverSound(string value)
{
if (!string.IsNullOrEmpty(value))
{
- var hoverResource = _cache.GetResource(value);
+ var hoverResource = GetSoundOrFallback(value, CCVars.UIHoverSound.DefaultValue);
var hoverSource =
_audioManager.CreateAudioSource(hoverResource);
@@ -95,4 +95,12 @@ private void SetHoverSound(string value)
UIManager.SetHoverSound(null);
}
}
+
+ private AudioResource GetSoundOrFallback(string path, string fallback)
+ {
+ if (!_cache.TryGetResource(path, out AudioResource? resource))
+ return _cache.GetResource(fallback);
+
+ return resource;
+ }
}
diff --git a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
index 84b787a4ec9..d60c978ccf5 100644
--- a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
+++ b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs
@@ -4,6 +4,7 @@
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Random;
+using Content.Shared.Random.Rules;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
index 60fe339069a..865dfc478d0 100644
--- a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
+++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
@@ -1,8 +1,7 @@
using Content.Shared.Audio.Jukebox;
using Robust.Client.Audio;
-using Robust.Client.Player;
+using Robust.Client.UserInterface;
using Robust.Shared.Audio.Components;
-using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Audio.Jukebox;
@@ -23,9 +22,7 @@ protected override void Open()
{
base.Open();
- _menu = new JukeboxMenu();
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
_menu.OnPlayPressed += args =>
{
@@ -100,19 +97,5 @@ public void SetTime(float time)
SendMessage(new JukeboxSetTimeMessage(sentTime));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- if (_menu == null)
- return;
-
- _menu.OnClose -= Close;
- _menu.Dispose();
- _menu = null;
- }
}
diff --git a/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs b/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs
index ffab1625483..09f3cec8fbf 100644
--- a/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs
+++ b/Content.Client/Bed/Cryostorage/CryostorageBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Bed.Cryostorage;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Bed.Cryostorage;
@@ -17,9 +18,7 @@ protected override void Open()
{
base.Open();
- _menu = new();
-
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.SlotRemoveButtonPressed += (ent, slot) =>
{
@@ -30,8 +29,6 @@ protected override void Open()
{
SendMessage(new CryostorageRemoveItemBuiMessage(ent, hand, CryostorageRemoveItemBuiMessage.RemovalType.Hand));
};
-
- _menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -45,12 +42,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
break;
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
index d3365702bcf..44c40143d83 100644
--- a/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
+++ b/Content.Client/Cargo/BUI/CargoBountyConsoleBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Cargo.BUI;
@@ -18,9 +19,7 @@ protected override void Open()
{
base.Open();
- _menu = new();
-
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.OnLabelButtonPressed += id =>
{
@@ -31,8 +30,6 @@ protected override void Open()
{
SendMessage(new BountySkipMessage(id));
};
-
- _menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState message)
@@ -44,14 +41,4 @@ protected override void UpdateState(BoundUserInterfaceState message)
_menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (!disposing)
- return;
-
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs
index 20c23a48a0d..2461dafb5f3 100644
--- a/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs
+++ b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Events;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Cargo.BUI;
@@ -18,21 +19,9 @@ protected override void Open()
{
base.Open();
- _menu = new CargoPalletMenu();
+ _menu = this.CreateWindow();
_menu.AppraiseRequested += OnAppraisal;
_menu.SellRequested += OnSell;
- _menu.OnClose += Close;
-
- _menu.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- {
- _menu?.Dispose();
- }
}
private void OnAppraisal()
diff --git a/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs
index 422d03707a0..02b721b9020 100644
--- a/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs
+++ b/Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Cargo.BUI;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Cargo.BUI;
@@ -9,6 +10,8 @@ namespace Content.Client.Cargo.BUI;
[UsedImplicitly]
public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
{
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+
[ViewVariables]
private CargoShuttleMenu? _menu;
@@ -19,24 +22,7 @@ public CargoShuttleConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base
protected override void Open()
{
base.Open();
- var collection = IoCManager.Instance;
-
- if (collection == null)
- return;
-
- _menu = new CargoShuttleMenu(collection.Resolve(), collection.Resolve().GetEntitySystem());
- _menu.OnClose += Close;
-
- _menu.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- {
- _menu?.Dispose();
- }
+ _menu = this.CreateWindow();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -45,6 +31,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return;
_menu?.SetAccountName(cargoState.AccountName);
_menu?.SetShuttleName(cargoState.ShuttleName);
- _menu?.SetOrders(cargoState.Orders);
+ _menu?.SetOrders(EntMan.System(), _protoManager, cargoState.Orders);
}
}
diff --git a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs
index c591f917da3..43b00089e16 100644
--- a/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs
+++ b/Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs
@@ -12,14 +12,9 @@ namespace Content.Client.Cargo.UI
[GenerateTypedNameReferences]
public sealed partial class CargoShuttleMenu : FancyWindow
{
- private readonly IPrototypeManager _protoManager;
- private readonly SpriteSystem _spriteSystem;
-
- public CargoShuttleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem)
+ public CargoShuttleMenu()
{
RobustXamlLoader.Load(this);
- _protoManager = protoManager;
- _spriteSystem = spriteSystem;
Title = Loc.GetString("cargo-shuttle-console-menu-title");
}
@@ -33,19 +28,19 @@ public void SetShuttleName(string name)
ShuttleNameLabel.Text = name;
}
- public void SetOrders(List orders)
+ public void SetOrders(SpriteSystem sprites, IPrototypeManager protoManager, List orders)
{
Orders.DisposeAllChildren();
foreach (var order in orders)
{
- var product = _protoManager.Index(order.ProductId);
+ var product = protoManager.Index(order.ProductId);
var productName = product.Name;
var row = new CargoOrderRow
{
Order = order,
- Icon = { Texture = _spriteSystem.Frame0(product) },
+ Icon = { Texture = sprites.Frame0(product) },
ProductName =
{
Text = Loc.GetString(
diff --git a/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
index f3b2d373d74..2d4d192ea8a 100644
--- a/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
+++ b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
@@ -31,7 +31,7 @@ public void UpdateState(NewsArticle article, int targetNum, int totalNum, bool n
Author.Visible = true;
PageName.Text = article.Title;
- PageText.SetMarkup(article.Content);
+ PageText.SetMarkupPermissive(article.Content);
PageNum.Text = $"{targetNum}/{totalNum}";
diff --git a/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs b/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
index d04c9d661dd..e89f7ab5002 100644
--- a/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
+++ b/Content.Client/Chat/TypingIndicator/TypingIndicatorVisualizerSystem.cs
@@ -2,21 +2,36 @@
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Prototypes;
+using Content.Shared.Inventory;
namespace Content.Client.Chat.TypingIndicator;
public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly InventorySystem _inventory = default!;
+
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
- if (!_prototypeManager.TryIndex(component.Prototype, out var proto))
+ var currentTypingIndicator = component.TypingIndicatorPrototype;
+
+ var evt = new BeforeShowTypingIndicatorEvent();
+
+ if (TryComp(uid, out var inventoryComp))
+ _inventory.RelayEvent((uid, inventoryComp), ref evt);
+
+ var overrideIndicator = evt.GetMostRecentIndicator();
+
+ if (overrideIndicator != null)
+ currentTypingIndicator = overrideIndicator.Value;
+
+ if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
{
- Log.Error($"Unknown typing indicator id: {component.Prototype}");
+ Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}");
return;
}
diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs
index 68c937a7885..adb61d10e62 100644
--- a/Content.Client/Chat/UI/SpeechBubble.cs
+++ b/Content.Client/Chat/UI/SpeechBubble.cs
@@ -16,6 +16,7 @@ public abstract class SpeechBubble : Control
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] protected readonly IConfigurationManager ConfigManager = default!;
+ private readonly SharedTransformSystem _transformSystem;
public enum SpeechType : byte
{
@@ -83,6 +84,7 @@ public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechSt
{
IoCManager.InjectDependencies(this);
_senderEntity = senderEntity;
+ _transformSystem = _entityManager.System();
// Use text clipping so new messages don't overlap old ones being pushed up.
RectClipContent = true;
@@ -140,7 +142,7 @@ protected override void FrameUpdate(FrameEventArgs args)
}
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset;
- var worldPos = xform.WorldPosition + offset;
+ var worldPos = _transformSystem.GetWorldPosition(xform) + offset;
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - new Vector2(ContentSize.X / 2, ContentSize.Y + _verticalOffsetAchieved);
diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
index 988fea7978b..3ef7f0ae73e 100644
--- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Containers.ItemSlots;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Chemistry.UI
{
@@ -27,13 +28,8 @@ protected override void Open()
base.Open();
// Setup window layout/elements
- _window = new ChemMasterWindow
- {
- Title = EntMan.GetComponent(Owner).EntityName,
- };
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.Title = EntMan.GetComponent(Owner).EntityName;
// Setup static button actions.
_window.InputEjectButton.OnPressed += _ => SendMessage(
@@ -75,15 +71,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(castState); // Update window state
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
}
}
diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
index 99e5a3d3953..2ad1b718887 100644
--- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Containers.ItemSlots;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Chemistry.UI
{
@@ -15,9 +16,6 @@ public sealed class ReagentDispenserBoundUserInterface : BoundUserInterface
[ViewVariables]
private ReagentDispenserWindow? _window;
- [ViewVariables]
- private ReagentDispenserBoundUserInterfaceState? _lastState;
-
public ReagentDispenserBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -32,14 +30,9 @@ protected override void Open()
base.Open();
// Setup window layout/elements
- _window = new()
- {
- Title = EntMan.GetComponent(Owner).EntityName,
- HelpGuidebookIds = EntMan.GetComponent(Owner).Guides
- };
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.Title = EntMan.GetComponent(Owner).EntityName;
+ _window.HelpGuidebookIds = EntMan.GetComponent(Owner).Guides;
// Setup static button actions.
_window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentDispenser.OutputSlotName));
@@ -63,19 +56,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
base.UpdateState(state);
var castState = (ReagentDispenserBoundUserInterfaceState) state;
- _lastState = castState;
-
_window?.UpdateState(castState); //Update window state
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
}
}
diff --git a/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs b/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs
index 35df131312d..1bc1c0dba9a 100644
--- a/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/TransferAmountBoundUserInterface.cs
@@ -1,24 +1,33 @@
using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Chemistry.UI
{
[UsedImplicitly]
public sealed class TransferAmountBoundUserInterface : BoundUserInterface
{
+ private IEntityManager _entManager;
+ private EntityUid _owner;
[ViewVariables]
private TransferAmountWindow? _window;
public TransferAmountBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
+ _owner = owner;
+ _entManager = IoCManager.Resolve();
}
protected override void Open()
{
base.Open();
- _window = new TransferAmountWindow();
+ _window = this.CreateWindow();
+
+ if (_entManager.TryGetComponent(_owner, out var comp))
+ _window.SetBounds(comp.MinimumTransferAmount.Int(), comp.MaximumTransferAmount.Int());
_window.ApplyButton.OnPressed += _ =>
{
@@ -28,15 +37,6 @@ protected override void Open()
_window.Close();
}
};
- _window.OnClose += Close;
- _window.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
}
}
}
diff --git a/Content.Client/Chemistry/UI/TransferAmountWindow.xaml b/Content.Client/Chemistry/UI/TransferAmountWindow.xaml
index 3d787c69c10..c73d86b10f1 100644
--- a/Content.Client/Chemistry/UI/TransferAmountWindow.xaml
+++ b/Content.Client/Chemistry/UI/TransferAmountWindow.xaml
@@ -6,6 +6,10 @@
+
+
+
+
diff --git a/Content.Client/Chemistry/UI/TransferAmountWindow.xaml.cs b/Content.Client/Chemistry/UI/TransferAmountWindow.xaml.cs
index 767e5cc8230..6bae0444415 100644
--- a/Content.Client/Chemistry/UI/TransferAmountWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/TransferAmountWindow.xaml.cs
@@ -8,9 +8,29 @@ namespace Content.Client.Chemistry.UI
[GenerateTypedNameReferences]
public sealed partial class TransferAmountWindow : DefaultWindow
{
+ private int _max = Int32.MaxValue;
+ private int _min = 1;
+
public TransferAmountWindow()
{
RobustXamlLoader.Load(this);
+ AmountLineEdit.OnTextChanged += OnValueChanged;
+ }
+
+ public void SetBounds(int min, int max)
+ {
+ _min = min;
+ _max = max;
+ MinimumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-min", ("amount", _min));
+ MaximumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-max", ("amount", _max));
+ }
+
+ private void OnValueChanged(LineEdit.LineEditEventArgs args)
+ {
+ if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount > _max || amount < _min)
+ ApplyButton.Disabled = true;
+ else
+ ApplyButton.Disabled = false;
}
}
}
diff --git a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs
index 17b88fb5a8f..010bfb31845 100644
--- a/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs
+++ b/Content.Client/Chemistry/Visualizers/SolutionContainerVisualsSystem.cs
@@ -2,6 +2,8 @@
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Clothing;
+using Content.Shared.Clothing.Components;
using Content.Shared.Hands;
using Content.Shared.Item;
using Content.Shared.Rounding;
@@ -20,6 +22,7 @@ public override void Initialize()
base.Initialize();
SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnGetHeldVisuals);
+ SubscribeLocalEvent(OnGetClothingVisuals);
}
private void OnMapInit(EntityUid uid, SolutionContainerVisualsComponent component, MapInitEvent args)
@@ -174,4 +177,41 @@ private void OnGetHeldVisuals(EntityUid uid, SolutionContainerVisualsComponent c
args.Layers.Add((key, layer));
}
}
+
+ private void OnGetClothingVisuals(Entity ent, ref GetEquipmentVisualsEvent args)
+ {
+ if (ent.Comp.EquippedFillBaseName == null)
+ return;
+
+ if (!TryComp(ent, out var appearance))
+ return;
+
+ if (!TryComp(ent, out var clothing))
+ return;
+
+ if (!AppearanceSystem.TryGetData(ent, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
+ return;
+
+ var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, ent.Comp.EquippedMaxFillLevels + 1);
+
+ if (closestFillSprite > 0)
+ {
+ var layer = new PrototypeLayerData();
+
+ var equippedPrefix = clothing.EquippedPrefix == null ? $"equipped-{args.Slot}" : $" {clothing.EquippedPrefix}-equipped-{args.Slot}";
+ var key = equippedPrefix + ent.Comp.EquippedFillBaseName + closestFillSprite;
+
+ // Make sure the sprite state is valid so we don't show a big red error message
+ // This saves us from having to make fill level sprites for every possible slot the item could be in (including pockets).
+ if (!TryComp(ent, out var sprite) || sprite.BaseRSI == null || !sprite.BaseRSI.TryGetState(key, out _))
+ return;
+
+ layer.State = key;
+
+ if (ent.Comp.ChangeColor && AppearanceSystem.TryGetData(ent, SolutionContainerVisuals.Color, out var color, appearance))
+ layer.Color = color;
+
+ args.Layers.Add((key, layer));
+ }
+ }
}
diff --git a/Content.Client/Clickable/ClickableComponent.cs b/Content.Client/Clickable/ClickableComponent.cs
index 6f75df46830..da81ed4c841 100644
--- a/Content.Client/Clickable/ClickableComponent.cs
+++ b/Content.Client/Clickable/ClickableComponent.cs
@@ -1,145 +1,17 @@
-using System.Numerics;
-using Robust.Client.GameObjects;
-using Robust.Client.Graphics;
-using Robust.Client.Utility;
-using Robust.Shared.Graphics;
-using static Robust.Client.GameObjects.SpriteComponent;
-using Direction = Robust.Shared.Maths.Direction;
+namespace Content.Client.Clickable;
-namespace Content.Client.Clickable
+[RegisterComponent]
+public sealed partial class ClickableComponent : Component
{
- [RegisterComponent]
- public sealed partial class ClickableComponent : Component
- {
- [Dependency] private readonly IClickMapManager _clickMapManager = default!;
-
- [DataField("bounds")] public DirBoundData? Bounds;
-
- ///
- /// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding
- /// boxes (see ). If that fails, attempts to use automatically generated click maps.
- ///
- /// The world position that was clicked.
- ///
- /// The draw depth for the sprite that captured the click.
- ///
- /// True if the click worked, false otherwise.
- public bool CheckClick(SpriteComponent sprite, TransformComponent transform, EntityQuery xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
- {
- if (!sprite.Visible)
- {
- drawDepth = default;
- renderOrder = default;
- bottom = default;
- return false;
- }
-
- drawDepth = sprite.DrawDepth;
- renderOrder = sprite.RenderOrder;
- var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
- var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
- bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
-
- Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
-
- // This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
- var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
-
- Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
-
- // First we get `localPos`, the clicked location in the sprite-coordinate frame.
- var entityXform = Matrix3Helpers.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
- var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
-
- // Check explicitly defined click-able bounds
- if (CheckDirBound(sprite, relativeRotation, localPos))
- return true;
-
- // Next check each individual sprite layer using automatically computed click maps.
- foreach (var spriteLayer in sprite.AllLayers)
- {
- if (!spriteLayer.Visible || spriteLayer is not Layer layer)
- continue;
-
- // Check the layer's texture, if it has one
- if (layer.Texture != null)
- {
- // Convert to image coordinates
- var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f);
-
- if (_clickMapManager.IsOccluding(layer.Texture, imagePos))
- return true;
- }
-
- // Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
- if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState))
- continue;
-
- var dir = Layer.GetDirection(rsiState.RsiDirections, relativeRotation);
+ [DataField] public DirBoundData? Bounds;
- // convert to layer-local coordinates
- layer.GetLayerDrawMatrix(dir, out var matrix);
- Matrix3x2.Invert(matrix, out var inverseMatrix);
- var layerLocal = Vector2.Transform(localPos, inverseMatrix);
-
- // Convert to image coordinates
- var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);
-
- // Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen.
- // This **can** differ from the dir defined before, but can also just be the same.
- if (sprite.EnableDirectionOverride)
- dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections);
- dir = dir.OffsetRsiDir(layer.DirOffset);
-
- if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos))
- return true;
- }
-
- drawDepth = default;
- renderOrder = default;
- bottom = default;
- return false;
- }
-
- public bool CheckDirBound(SpriteComponent sprite, Angle relativeRotation, Vector2 localPos)
- {
- if (Bounds == null)
- return false;
-
- // These explicit bounds only work for either 1 or 4 directional sprites.
-
- // This would be the orientation of a 4-directional sprite.
- var direction = relativeRotation.GetCardinalDir();
-
- var modLocalPos = sprite.NoRotation
- ? localPos
- : direction.ToAngle().RotateVec(localPos);
-
- // First, check the bounding box that is valid for all orientations
- if (Bounds.All.Contains(modLocalPos))
- return true;
-
- // Next, get and check the appropriate bounding box for the current sprite orientation
- var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch
- {
- Direction.East => Bounds.East,
- Direction.North => Bounds.North,
- Direction.South => Bounds.South,
- Direction.West => Bounds.West,
- _ => throw new InvalidOperationException()
- };
-
- return boundsForDir.Contains(modLocalPos);
- }
-
- [DataDefinition]
- public sealed partial class DirBoundData
- {
- [DataField("all")] public Box2 All;
- [DataField("north")] public Box2 North;
- [DataField("south")] public Box2 South;
- [DataField("east")] public Box2 East;
- [DataField("west")] public Box2 West;
- }
+ [DataDefinition]
+ public sealed partial class DirBoundData
+ {
+ [DataField] public Box2 All;
+ [DataField] public Box2 North;
+ [DataField] public Box2 South;
+ [DataField] public Box2 East;
+ [DataField] public Box2 West;
}
}
diff --git a/Content.Client/Clickable/ClickableSystem.cs b/Content.Client/Clickable/ClickableSystem.cs
new file mode 100644
index 00000000000..15d13df625c
--- /dev/null
+++ b/Content.Client/Clickable/ClickableSystem.cs
@@ -0,0 +1,168 @@
+using System.Numerics;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.Utility;
+using Robust.Shared.Graphics;
+
+namespace Content.Client.Clickable;
+
+///
+/// Handles click detection for sprites.
+///
+public sealed class ClickableSystem : EntitySystem
+{
+ [Dependency] private readonly IClickMapManager _clickMapManager = default!;
+ [Dependency] private readonly SharedTransformSystem _transforms = default!;
+ [Dependency] private readonly SpriteSystem _sprites = default!;
+
+ private EntityQuery _clickableQuery;
+ private EntityQuery _xformQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _clickableQuery = GetEntityQuery();
+ _xformQuery = GetEntityQuery();
+ }
+
+ ///
+ /// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding
+ /// boxes (see ). If that fails, attempts to use automatically generated click maps.
+ ///
+ /// The world position that was clicked.
+ ///
+ /// The draw depth for the sprite that captured the click.
+ ///
+ /// True if the click worked, false otherwise.
+ public bool CheckClick(Entity entity, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
+ {
+ if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false))
+ {
+ drawDepth = default;
+ renderOrder = default;
+ bottom = default;
+ return false;
+ }
+
+ if (!_xformQuery.Resolve(entity.Owner, ref entity.Comp3))
+ {
+ drawDepth = default;
+ renderOrder = default;
+ bottom = default;
+ return false;
+ }
+
+ var sprite = entity.Comp2;
+ var transform = entity.Comp3;
+
+ if (!sprite.Visible)
+ {
+ drawDepth = default;
+ renderOrder = default;
+ bottom = default;
+ return false;
+ }
+
+ drawDepth = sprite.DrawDepth;
+ renderOrder = sprite.RenderOrder;
+ var (spritePos, spriteRot) = _transforms.GetWorldPositionRotation(transform);
+ var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
+ bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
+
+ Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
+
+ // This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
+ var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
+
+ var cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
+
+ // First we get `localPos`, the clicked location in the sprite-coordinate frame.
+ var entityXform = Matrix3Helpers.CreateInverseTransform(spritePos, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
+ var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
+
+ // Check explicitly defined click-able bounds
+ if (CheckDirBound((entity.Owner, entity.Comp1, entity.Comp2), relativeRotation, localPos))
+ return true;
+
+ // Next check each individual sprite layer using automatically computed click maps.
+ foreach (var spriteLayer in sprite.AllLayers)
+ {
+ if (spriteLayer is not SpriteComponent.Layer layer || !_sprites.IsVisible(layer))
+ {
+ continue;
+ }
+
+ // Check the layer's texture, if it has one
+ if (layer.Texture != null)
+ {
+ // Convert to image coordinates
+ var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f);
+
+ if (_clickMapManager.IsOccluding(layer.Texture, imagePos))
+ return true;
+ }
+
+ // Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
+ if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState))
+ continue;
+
+ var dir = SpriteComponent.Layer.GetDirection(rsiState.RsiDirections, relativeRotation);
+
+ // convert to layer-local coordinates
+ layer.GetLayerDrawMatrix(dir, out var matrix);
+ Matrix3x2.Invert(matrix, out var inverseMatrix);
+ var layerLocal = Vector2.Transform(localPos, inverseMatrix);
+
+ // Convert to image coordinates
+ var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);
+
+ // Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen.
+ // This **can** differ from the dir defined before, but can also just be the same.
+ if (sprite.EnableDirectionOverride)
+ dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections);
+ dir = dir.OffsetRsiDir(layer.DirOffset);
+
+ if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos))
+ return true;
+ }
+
+ drawDepth = default;
+ renderOrder = default;
+ bottom = default;
+ return false;
+ }
+
+ public bool CheckDirBound(Entity entity, Angle relativeRotation, Vector2 localPos)
+ {
+ var clickable = entity.Comp1;
+ var sprite = entity.Comp2;
+
+ if (clickable.Bounds == null)
+ return false;
+
+ // These explicit bounds only work for either 1 or 4 directional sprites.
+
+ // This would be the orientation of a 4-directional sprite.
+ var direction = relativeRotation.GetCardinalDir();
+
+ var modLocalPos = sprite.NoRotation
+ ? localPos
+ : direction.ToAngle().RotateVec(localPos);
+
+ // First, check the bounding box that is valid for all orientations
+ if (clickable.Bounds.All.Contains(modLocalPos))
+ return true;
+
+ // Next, get and check the appropriate bounding box for the current sprite orientation
+ var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch
+ {
+ Direction.East => clickable.Bounds.East,
+ Direction.North => clickable.Bounds.North,
+ Direction.South => clickable.Bounds.South,
+ Direction.West => clickable.Bounds.West,
+ _ => throw new InvalidOperationException()
+ };
+
+ return boundsForDir.Contains(modLocalPos);
+ }
+}
diff --git a/Content.Client/Clock/ClockSystem.cs b/Content.Client/Clock/ClockSystem.cs
new file mode 100644
index 00000000000..7097ada9df6
--- /dev/null
+++ b/Content.Client/Clock/ClockSystem.cs
@@ -0,0 +1,26 @@
+using Content.Shared.Clock;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Clock;
+
+public sealed class ClockSystem : SharedClockSystem
+{
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp, out var sprite))
+ {
+ if (!sprite.LayerMapTryGet(ClockVisualLayers.HourHand, out var hourLayer) ||
+ !sprite.LayerMapTryGet(ClockVisualLayers.MinuteHand, out var minuteLayer))
+ continue;
+
+ var time = GetClockTime((uid, comp));
+ var hourState = $"{comp.HoursBase}{time.Hours % 12}";
+ var minuteState = $"{comp.MinutesBase}{time.Minutes / 5}";
+ sprite.LayerSetState(hourLayer, hourState);
+ sprite.LayerSetState(minuteLayer, minuteState);
+ }
+ }
+}
diff --git a/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs b/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs
index 26f0994701e..62a02f37186 100644
--- a/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs
+++ b/Content.Client/CloningConsole/UI/CloningConsoleBoundUserInterface.cs
@@ -1,6 +1,7 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Content.Shared.Cloning.CloningConsole;
+using Robust.Client.UserInterface;
namespace Content.Client.CloningConsole.UI
{
@@ -17,13 +18,11 @@ public CloningConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne
protected override void Open()
{
base.Open();
- _window = new CloningConsoleWindow
- {
- Title = Loc.GetString("cloning-console-window-title")
- };
- _window.OnClose += Close;
+
+ _window = this.CreateWindow();
+ _window.Title = Loc.GetString("cloning-console-window-title");
+
_window.CloneButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone));
- _window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -32,19 +31,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.Populate((CloningConsoleBoundUserInterfaceState) state);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- if (_window != null)
- {
- _window.OnClose -= Close;
- _window.CloneButton.OnPressed -= _ => SendMessage(new UiButtonPressedMessage(UiButton.Clone));
- }
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs
index 1c0d831226d..96bbcc54f2a 100644
--- a/Content.Client/Clothing/ClientClothingSystem.cs
+++ b/Content.Client/Clothing/ClientClothingSystem.cs
@@ -1,10 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
+using Content.Client.DisplacementMap;
using Content.Client.Inventory;
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
+using Content.Shared.DisplacementMap;
using Content.Shared.Humanoid;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
@@ -50,6 +52,7 @@ public sealed class ClientClothingSystem : ClothingSystem
[Dependency] private readonly IResourceCache _cache = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [Dependency] private readonly DisplacementMapSystem _displacement = default!;
public override void Initialize()
{
@@ -64,15 +67,14 @@ public override void Initialize()
private void OnAppearanceUpdate(EntityUid uid, InventoryComponent component, ref AppearanceChangeEvent args)
{
- // May need to update jumpsuit stencils if the sex changed. Also required to properly set the stencil on init
+ // May need to update displacement maps if the sex changed. Also required to properly set the stencil on init
if (args.Sprite == null)
return;
- if (_inventorySystem.TryGetSlotEntity(uid, Jumpsuit, out var suit, component)
- && TryComp(suit, out ClothingComponent? clothing))
+ var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
+ while (enumerator.NextItem(out var item, out var slot))
{
- SetGenderedMask(uid, args.Sprite, clothing);
- return;
+ RenderEquipment(uid, item, slot.Name, component);
}
// No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip.
@@ -179,14 +181,6 @@ private void OnVisualsChanged(EntityUid uid, InventoryComponent component, Visua
private void OnDidUnequip(EntityUid uid, SpriteComponent component, DidUnequipEvent args)
{
- // Hide jumpsuit mask layer.
- if (args.Slot == Jumpsuit
- && TryComp(uid, out SpriteComponent? sprite)
- && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var maskLayer))
- {
- sprite.LayerSetVisible(maskLayer, false);
- }
-
if (!TryComp(uid, out InventorySlotsComponent? inventorySlots))
return;
@@ -231,9 +225,6 @@ private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot
return;
}
- if (slot == Jumpsuit)
- SetGenderedMask(equipee, sprite, clothingComponent);
-
if (!_inventorySystem.TryGetSlot(equipee, slot, out var slotDef, inventory))
return;
@@ -265,7 +256,25 @@ private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot
// temporary, until layer draw depths get added. Basically: a layer with the key "slot" is being used as a
// bookmark to determine where in the list of layers we should insert the clothing layers.
bool slotLayerExists = sprite.LayerMapTryGet(slot, out var index);
- var displacementData = inventory.Displacements.GetValueOrDefault(slot);
+
+ // Select displacement maps
+ var displacementData = inventory.Displacements.GetValueOrDefault(slot); //Default unsexed map
+
+ var equipeeSex = CompOrNull(equipee)?.Sex;
+ if (equipeeSex != null)
+ {
+ switch (equipeeSex)
+ {
+ case Sex.Male:
+ if (inventory.MaleDisplacements.Count > 0)
+ displacementData = inventory.MaleDisplacements.GetValueOrDefault(slot);
+ break;
+ case Sex.Female:
+ if (inventory.FemaleDisplacements.Count > 0)
+ displacementData = inventory.FemaleDisplacements.GetValueOrDefault(slot);
+ break;
+ }
+ }
// add the new layers
foreach (var (key, layerData) in ev.Layers)
@@ -292,7 +301,7 @@ private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot
index = sprite.LayerMapReserveBlank(key);
if (sprite[index] is not Layer layer)
- return;
+ continue;
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
if (layerData.RsiPath == null
@@ -303,78 +312,19 @@ private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot
layer.SetRsi(clothingSprite.BaseRSI);
}
- // Another "temporary" fix for clothing stencil masks.
- // Sprite layer redactor when
- // Sprite "redactor" just a week away.
- if (slot == Jumpsuit)
- layerData.Shader ??= "StencilDraw";
-
sprite.LayerSetData(index, layerData);
layer.Offset += slotDef.Offset;
- if (displacementData != null)
+ if (displacementData is not null)
{
- if (displacementData.ShaderOverride != null)
- sprite.LayerSetShader(index, displacementData.ShaderOverride);
-
- var displacementKey = $"{key}-displacement";
- if (!revealedLayers.Add(displacementKey))
- {
- Log.Warning($"Duplicate key for clothing visuals DISPLACEMENT: {displacementKey}.");
+ //Checking that the state is not tied to the current race. In this case we don't need to use the displacement maps.
+ if (layerData.State is not null && inventory.SpeciesId is not null && layerData.State.EndsWith(inventory.SpeciesId))
continue;
- }
-
- var displacementLayer = _serialization.CreateCopy(displacementData.Layer, notNullableOverride: true);
- displacementLayer.CopyToShaderParameters!.LayerKey = key;
- // Add before main layer for this item.
- sprite.AddLayer(displacementLayer, index);
- sprite.LayerMapSet(displacementKey, index);
-
- revealedLayers.Add(displacementKey);
+ _displacement.TryAddDisplacement(displacementData, sprite, index, key, revealedLayers);
}
}
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers), true);
}
-
-
- ///
- /// Sets a sprite's gendered mask based on gender (obviously).
- ///
- /// Sprite to modify
- /// Humanoid, to get gender from
- /// Clothing component, to get mask sprite type
- private void SetGenderedMask(EntityUid uid, SpriteComponent sprite, ClothingComponent clothing)
- {
- if (!sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
- return;
-
- ClothingMask mask;
- string prefix;
-
- switch (CompOrNull(uid)?.Sex)
- {
- case Sex.Male:
- mask = clothing.MaleMask;
- prefix = "male_";
- break;
- case Sex.Female:
- mask = clothing.FemaleMask;
- prefix = "female_";
- break;
- default:
- mask = clothing.UnisexMask;
- prefix = "unisex_";
- break;
- }
-
- sprite.LayerSetState(layer, mask switch
- {
- ClothingMask.NoMask => $"{prefix}none",
- ClothingMask.UniformTop => $"{prefix}top",
- _ => $"{prefix}full",
- });
- sprite.LayerSetVisible(layer, true);
- }
}
diff --git a/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs b/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs
index 5b0d5fcf21f..83f6ba15662 100644
--- a/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs
+++ b/Content.Client/Clothing/UI/ChameleonBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Clothing.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Clothing.UI;
@@ -22,10 +23,8 @@ protected override void Open()
{
base.Open();
- _menu = new ChameleonMenu();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.OnIdSelected += OnIdSelected;
- _menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -42,15 +41,4 @@ private void OnIdSelected(string selectedId)
{
SendMessage(new ChameleonPrototypeSelectedMessage(selectedId));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _menu?.Close();
- _menu = null;
- }
- }
}
diff --git a/Content.Client/Commands/HideMechanismsCommand.cs b/Content.Client/Commands/HideMechanismsCommand.cs
index 5f9afc78b98..97d792628a6 100644
--- a/Content.Client/Commands/HideMechanismsCommand.cs
+++ b/Content.Client/Commands/HideMechanismsCommand.cs
@@ -30,7 +30,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args)
sprite.ContainerOccluded = false;
var tempParent = uid;
- while (containerSys.TryGetContainingContainer(tempParent, out var container))
+ while (containerSys.TryGetContainingContainer((tempParent, null, null), out var container))
{
if (!container.ShowContents)
{
diff --git a/Content.Client/Commands/ZoomCommand.cs b/Content.Client/Commands/ZoomCommand.cs
index 2bdc85e1fee..c63eeea8369 100644
--- a/Content.Client/Commands/ZoomCommand.cs
+++ b/Content.Client/Commands/ZoomCommand.cs
@@ -20,7 +20,7 @@ public sealed class ZoomCommand : LocalizedCommands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
Vector2 zoom;
- if (args.Length is not (1 or 2))
+ if (args.Length is not (1 or 2 or 3))
{
shell.WriteLine(Help);
return;
@@ -57,11 +57,18 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args)
}
}
+ var scalePvs = true;
+ if (args.Length == 3 && !bool.TryParse(args[2], out scalePvs))
+ {
+ shell.WriteError(LocalizationManager.GetString("cmd-parse-failure-bool", ("arg", args[2])));
+ return;
+ }
+
var player = _playerManager.LocalSession?.AttachedEntity;
if (_entityManager.TryGetComponent(player, out var content))
{
- _entityManager.System().RequestZoom(player.Value, zoom, true, content);
+ _entityManager.System().RequestZoom(player.Value, zoom, true, scalePvs, content);
return;
}
diff --git a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
index 1c94d32bf8d..0310e91eeb0 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
+++ b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Communications;
+using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
using Robust.Shared.Timing;
@@ -8,34 +9,11 @@ namespace Content.Client.Communications.UI
{
public sealed class CommunicationsConsoleBoundUserInterface : BoundUserInterface
{
- [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[ViewVariables]
private CommunicationsConsoleMenu? _menu;
- [ViewVariables]
- public bool CanAnnounce { get; private set; }
- [ViewVariables]
- public bool CanBroadcast { get; private set; }
-
- [ViewVariables]
- public bool CanCall { get; private set; }
-
- [ViewVariables]
- public bool CountdownStarted { get; private set; }
-
- [ViewVariables]
- public bool AlertLevelSelectable { get; private set; }
-
- [ViewVariables]
- public string CurrentLevel { get; private set; } = default!;
-
- [ViewVariables]
- private TimeSpan? _expectedCountdownTime;
-
- public int Countdown => _expectedCountdownTime == null ? 0 : Math.Max((int) _expectedCountdownTime.Value.Subtract(_gameTiming.CurTime).TotalSeconds, 0);
-
public CommunicationsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -44,23 +22,25 @@ protected override void Open()
{
base.Open();
- _menu = new CommunicationsConsoleMenu(this);
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnAnnounce += AnnounceButtonPressed;
+ _menu.OnBroadcast += BroadcastButtonPressed;
+ _menu.OnAlertLevel += AlertLevelSelected;
+ _menu.OnEmergencyLevel += EmergencyShuttleButtonPressed;
}
public void AlertLevelSelected(string level)
{
- if (AlertLevelSelectable)
+ if (_menu!.AlertLevelSelectable)
{
- CurrentLevel = level;
+ _menu.CurrentLevel = level;
SendMessage(new CommunicationsConsoleSelectAlertLevelMessage(level));
}
}
public void EmergencyShuttleButtonPressed()
{
- if (CountdownStarted)
+ if (_menu!.CountdownStarted)
RecallShuttle();
else
CallShuttle();
@@ -95,31 +75,23 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (state is not CommunicationsConsoleInterfaceState commsState)
return;
- CanAnnounce = commsState.CanAnnounce;
- CanBroadcast = commsState.CanBroadcast;
- CanCall = commsState.CanCall;
- _expectedCountdownTime = commsState.ExpectedCountdownEnd;
- CountdownStarted = commsState.CountdownStarted;
- AlertLevelSelectable = commsState.AlertLevels != null && !float.IsNaN(commsState.CurrentAlertDelay) && commsState.CurrentAlertDelay <= 0;
- CurrentLevel = commsState.CurrentAlert;
-
if (_menu != null)
{
+ _menu.CanAnnounce = commsState.CanAnnounce;
+ _menu.CanBroadcast = commsState.CanBroadcast;
+ _menu.CanCall = commsState.CanCall;
+ _menu.CountdownStarted = commsState.CountdownStarted;
+ _menu.AlertLevelSelectable = commsState.AlertLevels != null && !float.IsNaN(commsState.CurrentAlertDelay) && commsState.CurrentAlertDelay <= 0;
+ _menu.CurrentLevel = commsState.CurrentAlert;
+ _menu.CountdownEnd = commsState.ExpectedCountdownEnd;
+
_menu.UpdateCountdown();
- _menu.UpdateAlertLevels(commsState.AlertLevels, CurrentLevel);
- _menu.AlertLevelButton.Disabled = !AlertLevelSelectable;
- _menu.EmergencyShuttleButton.Disabled = !CanCall;
- _menu.AnnounceButton.Disabled = !CanAnnounce;
- _menu.BroadcastButton.Disabled = !CanBroadcast;
+ _menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel);
+ _menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable;
+ _menu.EmergencyShuttleButton.Disabled = !_menu.CanCall;
+ _menu.AnnounceButton.Disabled = !_menu.CanAnnounce;
+ _menu.BroadcastButton.Disabled = !_menu.CanBroadcast;
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
-
- _menu?.Dispose();
- }
}
}
diff --git a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
index 4d8dd86a4dc..56604ba526d 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
+++ b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
@@ -1,31 +1,40 @@
-using Content.Client.UserInterface.Controls;
-using System.Threading;
+using System.Globalization;
+using Content.Client.UserInterface.Controls;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
+using Robust.Shared.Timing;
using Robust.Shared.Utility;
-using Timer = Robust.Shared.Timing.Timer;
namespace Content.Client.Communications.UI
{
[GenerateTypedNameReferences]
public sealed partial class CommunicationsConsoleMenu : FancyWindow
{
- private CommunicationsConsoleBoundUserInterface Owner { get; set; }
- private readonly CancellationTokenSource _timerCancelTokenSource = new();
-
[Dependency] private readonly IConfigurationManager _cfg = default!;
-
- public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly ILocalizationManager _loc = default!;
+
+ public bool CanAnnounce;
+ public bool CanBroadcast;
+ public bool CanCall;
+ public bool AlertLevelSelectable;
+ public bool CountdownStarted;
+ public string CurrentLevel = string.Empty;
+ public TimeSpan? CountdownEnd;
+
+ public event Action? OnEmergencyLevel;
+ public event Action? OnAlertLevel;
+ public event Action? OnAnnounce;
+ public event Action? OnBroadcast;
+
+ public CommunicationsConsoleMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
- Owner = owner;
-
- var loc = IoCManager.Resolve();
- MessageInput.Placeholder = new Rope.Leaf(loc.GetString("comms-console-menu-announcement-placeholder"));
+ MessageInput.Placeholder = new Rope.Leaf(_loc.GetString("comms-console-menu-announcement-placeholder"));
var maxAnnounceLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
MessageInput.OnTextChanged += (args) =>
@@ -37,33 +46,38 @@ public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
}
else
{
- AnnounceButton.Disabled = !owner.CanAnnounce;
+ AnnounceButton.Disabled = !CanAnnounce;
AnnounceButton.ToolTip = null;
}
};
- AnnounceButton.OnPressed += (_) => Owner.AnnounceButtonPressed(Rope.Collapse(MessageInput.TextRope));
- AnnounceButton.Disabled = !owner.CanAnnounce;
+ AnnounceButton.OnPressed += _ => OnAnnounce?.Invoke(Rope.Collapse(MessageInput.TextRope));
+ AnnounceButton.Disabled = !CanAnnounce;
- BroadcastButton.OnPressed += (_) => Owner.BroadcastButtonPressed(Rope.Collapse(MessageInput.TextRope));
- BroadcastButton.Disabled = !owner.CanBroadcast;
+ BroadcastButton.OnPressed += _ => OnBroadcast?.Invoke(Rope.Collapse(MessageInput.TextRope));
+ BroadcastButton.Disabled = !CanBroadcast;
AlertLevelButton.OnItemSelected += args =>
{
var metadata = AlertLevelButton.GetItemMetadata(args.Id);
if (metadata != null && metadata is string cast)
{
- Owner.AlertLevelSelected(cast);
+ OnAlertLevel?.Invoke(cast);
}
};
- AlertLevelButton.Disabled = !owner.AlertLevelSelectable;
- EmergencyShuttleButton.OnPressed += (_) => Owner.EmergencyShuttleButtonPressed();
- EmergencyShuttleButton.Disabled = !owner.CanCall;
+ AlertLevelButton.Disabled = !AlertLevelSelectable;
+
+ EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke();
+ EmergencyShuttleButton.Disabled = !CanCall;
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
UpdateCountdown();
- Timer.SpawnRepeating(1000, UpdateCountdown, _timerCancelTokenSource.Token);
}
// The current alert could make levels unselectable, so we need to ensure that the UI reacts properly.
@@ -105,30 +119,19 @@ public void UpdateAlertLevels(List? alerts, string currentAlert)
public void UpdateCountdown()
{
- if (!Owner.CountdownStarted)
+ if (!CountdownStarted)
{
- CountdownLabel.SetMessage("");
+ CountdownLabel.SetMessage(string.Empty);
EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-call-shuttle");
return;
}
- EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle");
- CountdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s");
- }
-
- public override void Close()
- {
- base.Close();
-
- _timerCancelTokenSource.Cancel();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
+ var diff = MathHelper.Max((CountdownEnd - _timing.CurTime) ?? TimeSpan.Zero, TimeSpan.Zero);
- if (disposing)
- _timerCancelTokenSource.Cancel();
+ EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle");
+ var infoText = Loc.GetString($"comms-console-menu-time-remaining",
+ ("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
+ CountdownLabel.SetMessage(infoText);
}
}
}
diff --git a/Content.Client/Computer/ComputerBoundUserInterface.cs b/Content.Client/Computer/ComputerBoundUserInterface.cs
index bdbfe03fa10..11c26b252e9 100644
--- a/Content.Client/Computer/ComputerBoundUserInterface.cs
+++ b/Content.Client/Computer/ComputerBoundUserInterface.cs
@@ -1,4 +1,5 @@
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls;
namespace Content.Client.Computer
@@ -19,10 +20,8 @@ protected override void Open()
{
base.Open();
- _window = (TWindow) _dynamicTypeFactory.CreateInstance(typeof(TWindow));
+ _window = this.CreateWindow();
_window.SetupComputerWindow(this);
- _window.OnClose += Close;
- _window.OpenCentered();
}
// Alas, this constructor has to be copied to the subclass. :(
@@ -42,16 +41,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.UpdateState((TState) state);
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
-
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
_window?.ReceiveMessage(message);
diff --git a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs
index 4fea44f2253..e4966f1ec43 100644
--- a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs
+++ b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs
@@ -1,5 +1,6 @@
using System.Text.RegularExpressions;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using static Content.Shared.Configurable.ConfigurationComponent;
namespace Content.Client.Configurable.UI
@@ -9,9 +10,6 @@ public sealed class ConfigurationBoundUserInterface : BoundUserInterface
[ViewVariables]
private ConfigurationMenu? _menu;
- [ViewVariables]
- public Regex? Validation { get; internal set; }
-
public ConfigurationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -19,10 +17,8 @@ public ConfigurationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner
protected override void Open()
{
base.Open();
- _menu = new ConfigurationMenu(this);
-
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnConfiguration += SendConfiguration;
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -30,9 +26,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
base.UpdateState(state);
if (state is not ConfigurationBoundUserInterfaceState configurationState)
- {
return;
- }
_menu?.Populate(configurationState);
}
@@ -41,9 +35,12 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
base.ReceiveMessage(message);
+ if (_menu == null)
+ return;
+
if (message is ValidationUpdateMessage msg)
{
- Validation = new Regex(msg.ValidationString, RegexOptions.Compiled);
+ _menu.Validation = new Regex(msg.ValidationString, RegexOptions.Compiled);
}
}
@@ -51,16 +48,5 @@ public void SendConfiguration(Dictionary config)
{
SendMessage(new ConfigurationUpdatedMessage(config));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing && _menu != null)
- {
- _menu.OnClose -= Close;
- _menu.Close();
- }
- }
}
}
diff --git a/Content.Client/Configurable/UI/ConfigurationMenu.cs b/Content.Client/Configurable/UI/ConfigurationMenu.cs
index cc24af28692..29217eef7be 100644
--- a/Content.Client/Configurable/UI/ConfigurationMenu.cs
+++ b/Content.Client/Configurable/UI/ConfigurationMenu.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Numerics;
+using System.Text.RegularExpressions;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
@@ -13,23 +14,25 @@ namespace Content.Client.Configurable.UI
{
public sealed class ConfigurationMenu : DefaultWindow
{
- public ConfigurationBoundUserInterface Owner { get; }
-
private readonly BoxContainer _column;
private readonly BoxContainer _row;
private readonly List<(string name, LineEdit input)> _inputs;
- public ConfigurationMenu(ConfigurationBoundUserInterface owner)
+ [ViewVariables]
+ public Regex? Validation { get; internal set; }
+
+ public event Action>? OnConfiguration;
+
+ public ConfigurationMenu()
{
MinSize = SetSize = new Vector2(300, 250);
- Owner = owner;
_inputs = new List<(string name, LineEdit input)>();
Title = Loc.GetString("configuration-menu-device-title");
- BoxContainer baseContainer = new BoxContainer
+ var baseContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
VerticalExpand = true,
@@ -116,14 +119,13 @@ public void Populate(ConfigurationBoundUserInterfaceState state)
private void OnConfirm(ButtonEventArgs args)
{
var config = GenerateDictionary(_inputs, "Text");
-
- Owner.SendConfiguration(config);
+ OnConfiguration?.Invoke(config);
Close();
}
private bool Validate(string value)
{
- return Owner.Validation == null || Owner.Validation.IsMatch(value);
+ return Validation?.IsMatch(value) != false;
}
private Dictionary GenerateDictionary(IEnumerable<(string name, LineEdit input)> inputs, string propertyName)
diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs
index 453658bebf9..f909b23423d 100644
--- a/Content.Client/Construction/ConstructionSystem.cs
+++ b/Content.Client/Construction/ConstructionSystem.cs
@@ -26,7 +26,6 @@ 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 ExamineSystemShared _examineSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -48,11 +47,11 @@ public override void Initialize()
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenCraftingMenu,
- new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction:true))
+ new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction: true))
.Bind(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleUse, outsidePrediction: true))
.Bind(ContentKeyFunctions.EditorFlipObject,
- new PointerInputCmdHandler(HandleFlip, outsidePrediction:true))
+ new PointerInputCmdHandler(HandleFlip, outsidePrediction: true))
.Register();
SubscribeLocalEvent(HandleConstructionGhostExamined);
@@ -196,7 +195,7 @@ public bool TrySpawnGhost(
if (GhostPresent(loc))
return false;
- var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem));
+ var predicate = GetPredicate(prototype.CanBuildInImpassable, _transformSystem.ToMapCoordinates(loc));
if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate))
return false;
diff --git a/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs b/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs
index 86f1b8b83c7..887492955e9 100644
--- a/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs
+++ b/Content.Client/Construction/UI/FlatpackCreatorBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Construction.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Construction.UI
{
@@ -17,8 +18,8 @@ protected override void Open()
{
base.Open();
- _menu = new FlatpackCreatorMenu(Owner);
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
_menu.PackButtonPressed += () =>
{
@@ -27,14 +28,5 @@ protected override void Open()
_menu.OpenCentered();
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Dispose();
- }
}
}
diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs
index 00261632378..2a386a03de6 100644
--- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs
+++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs
@@ -25,7 +25,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
private readonly MaterialStorageSystem _materialStorage;
private readonly SpriteSystem _spriteSystem;
- private readonly EntityUid _owner;
+ private EntityUid _owner;
[ValidatePrototypeId]
public const string NoBoardEffectId = "FlatpackerNoBoardEffect";
@@ -35,7 +35,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
public event Action? PackButtonPressed;
- public FlatpackCreatorMenu(EntityUid uid)
+ public FlatpackCreatorMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -45,14 +45,17 @@ public FlatpackCreatorMenu(EntityUid uid)
_materialStorage = _entityManager.System();
_spriteSystem = _entityManager.System();
- _owner = uid;
-
PackButton.OnPressed += _ => PackButtonPressed?.Invoke();
- MaterialStorageControl.SetOwner(uid);
InsertLabel.SetMarkup(Loc.GetString("flatpacker-ui-insert-board"));
}
+ public void SetEntity(EntityUid uid)
+ {
+ _owner = uid;
+ MaterialStorageControl.SetOwner(uid);
+ }
+
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs
index a60619baa35..0462c095ba8 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, _xform);
+ var coords = _xform.ToMapCoordinates(args.Coordinates);
if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities))
OpenRootMenu(entities);
diff --git a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs
index e2c4d51ecd1..e5be0b1811f 100644
--- a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs
+++ b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs
@@ -2,12 +2,15 @@
using Content.Shared.Crayon;
using Content.Shared.Decals;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Crayon.UI
{
public sealed class CrayonBoundUserInterface : BoundUserInterface
{
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+
[ViewVariables]
private CrayonWindow? _menu;
@@ -18,15 +21,29 @@ public CrayonBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey
protected override void Open()
{
base.Open();
- _menu = new CrayonWindow(this);
-
- _menu.OnClose += Close;
- var prototypeManager = IoCManager.Resolve();
- var crayonDecals = prototypeManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon"));
- _menu.Populate(crayonDecals);
+ _menu = this.CreateWindow();
+ _menu.OnColorSelected += SelectColor;
+ _menu.OnSelected += Select;
+ PopulateCrayons();
_menu.OpenCenteredLeft();
}
+ private void PopulateCrayons()
+ {
+ var crayonDecals = _protoManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon"));
+ _menu?.Populate(crayonDecals);
+ }
+
+ public override void OnProtoReload(PrototypesReloadedEventArgs args)
+ {
+ base.OnProtoReload(args);
+
+ if (!args.WasModified())
+ return;
+
+ PopulateCrayons();
+ }
+
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
@@ -43,16 +60,5 @@ public void SelectColor(Color color)
{
SendMessage(new CrayonColorMessage(color));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _menu?.Close();
- _menu = null;
- }
- }
}
}
diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs
index 2a5801ccf2d..6ef282d219a 100644
--- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs
+++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs
@@ -18,18 +18,17 @@ namespace Content.Client.Crayon.UI
[GenerateTypedNameReferences]
public sealed partial class CrayonWindow : DefaultWindow
{
- public CrayonBoundUserInterface Owner { get; }
-
private Dictionary? _decals;
private string? _selected;
private Color _color;
- public CrayonWindow(CrayonBoundUserInterface owner)
+ public event Action? OnColorSelected;
+ public event Action? OnSelected;
+
+ public CrayonWindow()
{
RobustXamlLoader.Load(this);
- Owner = owner;
-
Search.OnTextChanged += _ => RefreshList();
ColorSelector.OnColorChanged += SelectColor;
}
@@ -38,16 +37,16 @@ private void SelectColor(Color color)
{
_color = color;
- Owner.SelectColor(color);
-
+ OnColorSelected?.Invoke(color);
RefreshList();
}
private void RefreshList()
{
// Clear
- Grid.RemoveAllChildren();
- if (_decals == null) return;
+ Grid.DisposeAllChildren();
+ if (_decals == null)
+ return;
var filter = Search.Text;
foreach (var (decal, tex) in _decals)
@@ -89,8 +88,8 @@ private void ButtonOnPressed(ButtonEventArgs obj)
{
if (obj.Button.Name == null) return;
- Owner.Select(obj.Button.Name);
_selected = obj.Button.Name;
+ OnSelected?.Invoke(_selected);
RefreshList();
}
diff --git a/Content.Client/CrewManifest/UI/CrewManifestSection.cs b/Content.Client/CrewManifest/UI/CrewManifestSection.cs
index 4b1b02cb755..29cf850e2b9 100644
--- a/Content.Client/CrewManifest/UI/CrewManifestSection.cs
+++ b/Content.Client/CrewManifest/UI/CrewManifestSection.cs
@@ -51,7 +51,7 @@ public CrewManifestSection(
title.SetMessage(entry.JobTitle);
- if (prototypeManager.TryIndex(entry.JobIcon, out var jobIcon))
+ if (prototypeManager.TryIndex(entry.JobIcon, out var jobIcon))
{
var icon = new TextureRect()
{
diff --git a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs
index b259e08e723..21aa54c9622 100644
--- a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs
+++ b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs
@@ -7,10 +7,12 @@
using Content.Shared.StationRecords;
using Robust.Client.AutoGenerated;
using Robust.Client.Player;
+using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
+using System.Linq;
namespace Content.Client.CriminalRecords;
@@ -36,7 +38,6 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
public Action? OnDialogConfirmed;
private uint _maxLength;
- private bool _isPopulating;
private bool _access;
private uint? _selectedKey;
private CriminalRecord? _selectedRecord;
@@ -74,7 +75,7 @@ public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerMa
RecordListing.OnItemSelected += args =>
{
- if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast)
+ if (RecordListing[args.ItemIndex].Metadata is not uint cast)
return;
OnKeySelected?.Invoke(cast);
@@ -82,8 +83,7 @@ public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerMa
RecordListing.OnItemDeselected += _ =>
{
- if (!_isPopulating)
- OnKeySelected?.Invoke(null);
+ OnKeySelected?.Invoke(null);
};
FilterType.OnItemSelected += eventArgs =>
@@ -133,13 +133,8 @@ public void UpdateState(CriminalRecordsConsoleState state)
FilterType.SelectId((int)_currentFilterType);
- // set up the records listing panel
- RecordListing.Clear();
-
- var hasRecords = state.RecordListing != null && state.RecordListing.Count > 0;
- NoRecords.Visible = !hasRecords;
- if (hasRecords)
- PopulateRecordListing(state.RecordListing!);
+ NoRecords.Visible = state.RecordListing == null || state.RecordListing.Count == 0;
+ PopulateRecordListing(state.RecordListing);
// set up the selected person's record
var selected = _selectedKey != null;
@@ -167,19 +162,59 @@ public void UpdateState(CriminalRecordsConsoleState state)
}
}
- private void PopulateRecordListing(Dictionary listing)
+ private void PopulateRecordListing(Dictionary? listing)
{
- _isPopulating = true;
+ if (listing == null)
+ {
+ RecordListing.Clear();
+ return;
+ }
+
+ var entries = listing.ToList();
+ entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal));
+ // `entries` now contains the definitive list of items which should be in
+ // our list of records and is in the order we want to present those items.
+
+ // Walk through the existing items in RecordListing and in the updated listing
+ // in parallel to synchronize the items in RecordListing with `entries`.
+ int i = RecordListing.Count - 1;
+ int j = entries.Count - 1;
+ while(i >= 0 && j >= 0)
+ {
+ var strcmp = string.Compare(RecordListing[i].Text, entries[j].Value, StringComparison.Ordinal);
+ if (strcmp == 0)
+ {
+ // This item exists in both RecordListing and `entries`. Nothing to do.
+ i--;
+ j--;
+ }
+ else if (strcmp > 0)
+ {
+ // Item exists in RecordListing, but not in `entries`. Remove it.
+ RecordListing.RemoveAt(i);
+ i--;
+ }
+ else if (strcmp < 0)
+ {
+ // A new entry which doesn't exist in RecordListing. Create it.
+ RecordListing.Insert(i + 1, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key});
+ j--;
+ }
+ }
- foreach (var (key, name) in listing)
+ // Any remaining items in RecordListing don't exist in `entries`, so remove them
+ while (i >= 0)
{
- var item = RecordListing.AddItem(name);
- item.Metadata = key;
- item.Selected = key == _selectedKey;
+ RecordListing.RemoveAt(i);
+ i--;
}
- _isPopulating = false;
- RecordListing.SortItemsByText();
+ // And finally, any remaining items in `entries`, don't exist in RecordListing. Create them.
+ while (j >= 0)
+ {
+ RecordListing.Insert(0, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key});
+ j--;
+ }
}
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
@@ -211,10 +246,7 @@ private void AddStatusSelect(SecurityStatus status)
private void FilterListingOfRecords(string text = "")
{
- if (!_isPopulating)
- {
- OnFiltersChanged?.Invoke(_currentFilterType, text);
- }
+ OnFiltersChanged?.Invoke(_currentFilterType, text);
}
private void SetStatus(SecurityStatus status)
diff --git a/Content.Client/DisplacementMap/DisplacementMapSystem.cs b/Content.Client/DisplacementMap/DisplacementMapSystem.cs
new file mode 100644
index 00000000000..6db164a09f0
--- /dev/null
+++ b/Content.Client/DisplacementMap/DisplacementMapSystem.cs
@@ -0,0 +1,65 @@
+using Content.Shared.DisplacementMap;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Serialization.Manager;
+
+namespace Content.Client.DisplacementMap;
+
+public sealed class DisplacementMapSystem : EntitySystem
+{
+ [Dependency] private readonly ISerializationManager _serialization = default!;
+
+ public bool TryAddDisplacement(DisplacementData data, SpriteComponent sprite, int index, string key, HashSet revealedLayers)
+ {
+ if (data.ShaderOverride != null)
+ sprite.LayerSetShader(index, data.ShaderOverride);
+
+ var displacementKey = $"{key}-displacement";
+ if (!revealedLayers.Add(displacementKey))
+ {
+ Log.Warning($"Duplicate key for DISPLACEMENT: {displacementKey}.");
+ return false;
+ }
+
+ //allows you not to write it every time in the YML
+ foreach (var pair in data.SizeMaps)
+ {
+ pair.Value.CopyToShaderParameters??= new()
+ {
+ LayerKey = "dummy",
+ ParameterTexture = "displacementMap",
+ ParameterUV = "displacementUV",
+ };
+ }
+
+ if (!data.SizeMaps.ContainsKey(32))
+ {
+ Log.Error($"DISPLACEMENT: {displacementKey} don't have 32x32 default displacement map");
+ return false;
+ }
+
+ // We choose a displacement map from the possible ones, matching the size with the original layer size.
+ // If there is no such a map, we use a standard 32 by 32 one
+ var displacementDataLayer = data.SizeMaps[EyeManager.PixelsPerMeter];
+ var actualRSI = sprite.LayerGetActualRSI(index);
+ if (actualRSI is not null)
+ {
+ if (actualRSI.Size.X != actualRSI.Size.Y)
+ Log.Warning($"DISPLACEMENT: {displacementKey} has a resolution that is not 1:1, things can look crooked");
+
+ var layerSize = actualRSI.Size.X;
+ if (data.SizeMaps.ContainsKey(layerSize))
+ displacementDataLayer = data.SizeMaps[layerSize];
+ }
+
+ var displacementLayer = _serialization.CreateCopy(displacementDataLayer, notNullableOverride: true);
+ displacementLayer.CopyToShaderParameters!.LayerKey = key;
+
+ sprite.AddLayer(displacementLayer, index);
+ sprite.LayerMapSet(displacementKey, index);
+
+ revealedLayers.Add(displacementKey);
+
+ return true;
+ }
+}
diff --git a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs
index e8e77217ea5..296e71d3a95 100644
--- a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs
+++ b/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs
@@ -1,5 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
namespace Content.Client.Disposal.UI
@@ -21,20 +22,16 @@ protected override void Open()
{
base.Open();
- _window = new DisposalRouterWindow();
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
-
}
private void ButtonPressed(UiAction action, string tag)
{
SendMessage(new UiActionMessage(action, tag));
- _window?.Close();
+ Close();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -48,18 +45,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
-
-
}
-
}
diff --git a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs
index 3aeed8dc802..7fc0eb85401 100644
--- a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs
+++ b/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs
@@ -1,5 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
namespace Content.Client.Disposal.UI
@@ -21,20 +22,17 @@ protected override void Open()
{
base.Open();
- _window = new DisposalTaggerWindow();
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
_window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text);
-
}
private void ButtonPressed(UiAction action, string tag)
{
+ // TODO: This looks copy-pasted with the other mailing stuff...
SendMessage(new UiActionMessage(action, tag));
- _window?.Close();
+ Close();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -48,18 +46,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Dispose();
- }
- }
-
-
}
-
}
diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs
index 797e20ae7d2..489d749a0cf 100644
--- a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs
+++ b/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs
@@ -2,6 +2,7 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
namespace Content.Client.Disposal.UI
{
@@ -11,6 +12,8 @@ namespace Content.Client.Disposal.UI
[GenerateTypedNameReferences]
public sealed partial class MailingUnitWindow : DefaultWindow
{
+ public TimeSpan FullPressure;
+
public MailingUnitWindow()
{
RobustXamlLoader.Load(this);
@@ -26,6 +29,7 @@ public bool UpdateState(MailingUnitBoundUserInterfaceState state)
Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", state.Tag ?? " "));
UnitState.Text = disposalState.UnitState;
+ FullPressure = disposalState.FullPressureTime;
var pressureReached = PressureBar.UpdatePressure(disposalState.FullPressureTime);
Power.Pressed = disposalState.Powered;
Engage.Pressed = disposalState.Engaged;
@@ -42,9 +46,10 @@ public bool UpdateState(MailingUnitBoundUserInterfaceState state)
return !disposalState.Powered || pressureReached;
}
- public bool UpdatePressure(TimeSpan stateFullPressureTime)
+ protected override void FrameUpdate(FrameEventArgs args)
{
- return PressureBar.UpdatePressure(stateFullPressureTime);
+ base.FrameUpdate(args);
+ PressureBar.UpdatePressure(FullPressure);
}
}
}
diff --git a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
index cd7ea717ce3..9b7e23c03aa 100644
--- a/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
+++ b/Content.Client/Doors/Electronics/DoorElectronicsBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Access;
using Content.Shared.Doors.Electronics;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Doors.Electronics;
@@ -18,6 +19,23 @@ public DoorElectronicsBoundUserInterface(EntityUid owner, Enum uiKey) : base(own
protected override void Open()
{
base.Open();
+ _window = this.CreateWindow();
+ _window.OnAccessChanged += UpdateConfiguration;
+ Reset();
+ }
+
+ public override void OnProtoReload(PrototypesReloadedEventArgs args)
+ {
+ base.OnProtoReload(args);
+
+ if (!args.WasModified())
+ return;
+
+ Reset();
+ }
+
+ private void Reset()
+ {
List> accessLevels = new();
foreach (var accessLevel in _prototypeManager.EnumeratePrototypes())
@@ -29,10 +47,7 @@ protected override void Open()
}
accessLevels.Sort();
-
- _window = new DoorElectronicsConfigurationMenu(this, accessLevels, _prototypeManager);
- _window.OnClose += Close;
- _window.OpenCentered();
+ _window?.Reset(_prototypeManager, accessLevels);
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -44,14 +59,6 @@ protected override void UpdateState(BoundUserInterfaceState 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.cs b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
index c01f13a462e..2112a562971 100644
--- a/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
+++ b/Content.Client/Doors/Electronics/DoorElectronicsConfigurationMenu.xaml.cs
@@ -15,22 +15,23 @@ namespace Content.Client.Doors.Electronics;
[GenerateTypedNameReferences]
public sealed partial class DoorElectronicsConfigurationMenu : FancyWindow
{
- private readonly DoorElectronicsBoundUserInterface _owner;
- private AccessLevelControl _buttonsList = new();
+ private readonly AccessLevelControl _buttonsList = new();
- public DoorElectronicsConfigurationMenu(DoorElectronicsBoundUserInterface ui, List> accessLevels, IPrototypeManager prototypeManager)
+ public event Action>>? OnAccessChanged;
+
+ public DoorElectronicsConfigurationMenu()
{
RobustXamlLoader.Load(this);
-
- _owner = ui;
-
- _buttonsList.Populate(accessLevels, prototypeManager);
AccessLevelControlContainer.AddChild(_buttonsList);
+ }
+
+ public void Reset(IPrototypeManager protoManager, List> accessLevels)
+ {
+ _buttonsList.Populate(accessLevels, protoManager);
- foreach (var (id, button) in _buttonsList.ButtonsList)
+ foreach (var button in _buttonsList.ButtonsList.Values)
{
- button.OnPressed += _ => _owner.UpdateConfiguration(
- _buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
+ button.OnPressed += _ => OnAccessChanged?.Invoke(_buttonsList.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
}
}
diff --git a/Content.Client/Drowsiness/DrowsinessOverlay.cs b/Content.Client/Drowsiness/DrowsinessOverlay.cs
new file mode 100644
index 00000000000..a316f31ae64
--- /dev/null
+++ b/Content.Client/Drowsiness/DrowsinessOverlay.cs
@@ -0,0 +1,80 @@
+using Content.Shared.Drowsiness;
+using Content.Shared.StatusEffect;
+using Robust.Client.Graphics;
+using Robust.Client.Player;
+using Robust.Shared.Enums;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Drowsiness;
+
+public sealed class DrowsinessOverlay : Overlay
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IEntitySystemManager _sysMan = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+
+ public override OverlaySpace Space => OverlaySpace.WorldSpace;
+ public override bool RequestScreenTexture => true;
+ private readonly ShaderInstance _drowsinessShader;
+
+ public float CurrentPower = 0.0f;
+
+ private const float PowerDivisor = 250.0f;
+ private const float Intensity = 0.2f; // for adjusting the visual scale
+ private float _visualScale = 0; // between 0 and 1
+
+ public DrowsinessOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _drowsinessShader = _prototypeManager.Index("Drowsiness").InstanceUnique();
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ var playerEntity = _playerManager.LocalEntity;
+
+ if (playerEntity == null)
+ return;
+
+ if (!_entityManager.HasComponent(playerEntity)
+ || !_entityManager.TryGetComponent(playerEntity, out var status))
+ return;
+
+ var statusSys = _sysMan.GetEntitySystem();
+ if (!statusSys.TryGetTime(playerEntity.Value, SharedDrowsinessSystem.DrowsinessKey, out var time, status))
+ return;
+
+ var curTime = _timing.CurTime;
+ var timeLeft = (float)(time.Value.Item2 - curTime).TotalSeconds;
+
+ CurrentPower += 8f * (0.5f * timeLeft - CurrentPower) * args.DeltaSeconds / (timeLeft + 1);
+ }
+
+ protected override bool BeforeDraw(in OverlayDrawArgs args)
+ {
+ if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
+ return false;
+
+ if (args.Viewport.Eye != eyeComp.Eye)
+ return false;
+
+ _visualScale = Math.Clamp(CurrentPower / PowerDivisor, 0.0f, 1.0f);
+ return _visualScale > 0;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ if (ScreenTexture == null)
+ return;
+
+ var handle = args.WorldHandle;
+ _drowsinessShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
+ _drowsinessShader.SetParameter("Strength", _visualScale * Intensity);
+ handle.UseShader(_drowsinessShader);
+ handle.DrawRect(args.WorldBounds, Color.White);
+ handle.UseShader(null);
+ }
+}
diff --git a/Content.Client/Drowsiness/DrowsinessSystem.cs b/Content.Client/Drowsiness/DrowsinessSystem.cs
new file mode 100644
index 00000000000..bc8862b19d2
--- /dev/null
+++ b/Content.Client/Drowsiness/DrowsinessSystem.cs
@@ -0,0 +1,53 @@
+using Content.Shared.Drowsiness;
+using Robust.Client.Graphics;
+using Robust.Client.Player;
+using Robust.Shared.Player;
+
+namespace Content.Client.Drowsiness;
+
+public sealed class DrowsinessSystem : SharedDrowsinessSystem
+{
+ [Dependency] private readonly IPlayerManager _player = default!;
+ [Dependency] private readonly IOverlayManager _overlayMan = default!;
+
+ private DrowsinessOverlay _overlay = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnDrowsinessInit);
+ SubscribeLocalEvent(OnDrowsinessShutdown);
+
+ SubscribeLocalEvent(OnPlayerAttached);
+ SubscribeLocalEvent(OnPlayerDetached);
+
+ _overlay = new();
+ }
+
+ private void OnPlayerAttached(EntityUid uid, DrowsinessComponent component, LocalPlayerAttachedEvent args)
+ {
+ _overlayMan.AddOverlay(_overlay);
+ }
+
+ private void OnPlayerDetached(EntityUid uid, DrowsinessComponent component, LocalPlayerDetachedEvent args)
+ {
+ _overlay.CurrentPower = 0;
+ _overlayMan.RemoveOverlay(_overlay);
+ }
+
+ private void OnDrowsinessInit(EntityUid uid, DrowsinessComponent component, ComponentInit args)
+ {
+ if (_player.LocalEntity == uid)
+ _overlayMan.AddOverlay(_overlay);
+ }
+
+ private void OnDrowsinessShutdown(EntityUid uid, DrowsinessComponent component, ComponentShutdown args)
+ {
+ if (_player.LocalEntity == uid)
+ {
+ _overlay.CurrentPower = 0;
+ _overlayMan.RemoveOverlay(_overlay);
+ }
+ }
+}
diff --git a/Content.Client/Explosion/ExplosionOverlay.cs b/Content.Client/Explosion/ExplosionOverlay.cs
index 8cf7447a5d8..a005d0317b1 100644
--- a/Content.Client/Explosion/ExplosionOverlay.cs
+++ b/Content.Client/Explosion/ExplosionOverlay.cs
@@ -16,15 +16,19 @@ public sealed class ExplosionOverlay : Overlay
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
+ private readonly SharedTransformSystem _transformSystem;
+ private SharedAppearanceSystem _appearance;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
private ShaderInstance _shader;
- public ExplosionOverlay()
+ public ExplosionOverlay(SharedAppearanceSystem appearanceSystem)
{
IoCManager.InjectDependencies(this);
_shader = _proto.Index("unshaded").Instance();
+ _transformSystem = _entMan.System();
+ _appearance = appearanceSystem;
}
protected override void Draw(in OverlayDrawArgs args)
@@ -33,15 +37,14 @@ protected override void Draw(in OverlayDrawArgs args)
drawHandle.UseShader(_shader);
var xforms = _entMan.GetEntityQuery();
- var query = _entMan
- .EntityQuery(true);
+ var query = _entMan.EntityQueryEnumerator();
- foreach (var (visuals, textures, appearance) in query)
+ while (query.MoveNext(out var uid, out var visuals, out var textures))
{
if (visuals.Epicenter.MapId != args.MapId)
continue;
- if (!appearance.TryGetData(ExplosionAppearanceData.Progress, out int index))
+ if (!_appearance.TryGetData(uid, ExplosionAppearanceData.Progress, out int index))
continue;
index = Math.Min(index, visuals.Intensity.Count - 1);
@@ -67,7 +70,7 @@ private void DrawExplosion(
continue;
var xform = xforms.GetComponent(gridId);
- var (_, _, worldMatrix, invWorldMatrix) = xform.GetWorldPositionRotationMatrixWithInv(xforms);
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(xform, xforms);
gridBounds = invWorldMatrix.TransformBox(worldBounds).Enlarged(grid.TileSize * 2);
drawHandle.SetTransform(worldMatrix);
diff --git a/Content.Client/Explosion/ExplosionOverlaySystem.cs b/Content.Client/Explosion/ExplosionOverlaySystem.cs
index fe552af0e79..7e43bbdc38c 100644
--- a/Content.Client/Explosion/ExplosionOverlaySystem.cs
+++ b/Content.Client/Explosion/ExplosionOverlaySystem.cs
@@ -20,6 +20,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly SharedPointLightSystem _lights = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public override void Initialize()
{
@@ -28,7 +29,7 @@ public override void Initialize()
SubscribeLocalEvent(OnExplosionInit);
SubscribeLocalEvent(OnCompRemove);
SubscribeLocalEvent(OnExplosionHandleState);
- _overlayMan.AddOverlay(new ExplosionOverlay());
+ _overlayMan.AddOverlay(new ExplosionOverlay(_appearance));
}
private void OnExplosionHandleState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentHandleState args)
diff --git a/Content.Client/Explosion/ExplosionSystem.cs b/Content.Client/Explosion/ExplosionSystem.cs
new file mode 100644
index 00000000000..a2ed2d50e0d
--- /dev/null
+++ b/Content.Client/Explosion/ExplosionSystem.cs
@@ -0,0 +1,8 @@
+using Content.Shared.Explosion.EntitySystems;
+
+namespace Content.Client.Explosion.EntitySystems;
+
+public sealed class ExplosionSystem : SharedExplosionSystem
+{
+
+}
diff --git a/Content.Client/Extinguisher/FireExtinguisherComponent.cs b/Content.Client/Extinguisher/FireExtinguisherComponent.cs
deleted file mode 100644
index 324b05a93d4..00000000000
--- a/Content.Client/Extinguisher/FireExtinguisherComponent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using Content.Shared.Extinguisher;
-using Robust.Shared.GameStates;
-
-namespace Content.Client.Extinguisher;
-
-[RegisterComponent]
-public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent;
diff --git a/Content.Client/Fax/AdminUI/AdminFaxEui.cs b/Content.Client/Fax/AdminUI/AdminFaxEui.cs
index ace3f3eb7b3..452c54eb797 100644
--- a/Content.Client/Fax/AdminUI/AdminFaxEui.cs
+++ b/Content.Client/Fax/AdminUI/AdminFaxEui.cs
@@ -16,7 +16,7 @@ public AdminFaxEui()
_window.OnClose += () => SendMessage(new AdminFaxEuiMsg.Close());
_window.OnFollowFax += entity => SendMessage(new AdminFaxEuiMsg.Follow(entity));
_window.OnMessageSend += args => SendMessage(new AdminFaxEuiMsg.Send(args.entity, args.title,
- args.stampedBy, args.message, args.stampSprite, args.stampColor));
+ args.stampedBy, args.message, args.stampSprite, args.stampColor, args.locked));
}
public override void Opened()
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
index d469a0e9d38..dc4092a3b53 100644
--- a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
+++ b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
@@ -23,7 +23,7 @@
-
-
+
+
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
index c1fba483094..698b3114b7d 100644
--- a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
+++ b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
@@ -14,7 +14,7 @@ public sealed partial class AdminFaxWindow : DefaultWindow
{
private const string StampsRsiPath = "/Textures/Objects/Misc/bureaucracy.rsi";
- public Action<(NetEntity entity, string title, string stampedBy, string message, string stampSprite, Color stampColor)>? OnMessageSend;
+ public Action<(NetEntity entity, string title, string stampedBy, string message, string stampSprite, Color stampColor, bool locked)>? OnMessageSend;
public Action? OnFollowFax;
[Dependency] private readonly IResourceCache _resCache = default!;
@@ -98,6 +98,7 @@ private void SendMessage(BaseButton.ButtonEventArgs obj)
var from = FromEdit.Text;
var stampColor = StampColorSelector.Color;
- OnMessageSend?.Invoke((faxEntity.Value, title, from, message, stamp, stampColor));
+ var locked = LockPageCheckbox.Pressed;
+ OnMessageSend?.Invoke((faxEntity.Value, title, from, message, stamp, stampColor, locked));
}
}
diff --git a/Content.Client/Fax/System/FaxVisualsSystem.cs b/Content.Client/Fax/System/FaxVisualsSystem.cs
index 892aec1d954..e752fbf48e6 100644
--- a/Content.Client/Fax/System/FaxVisualsSystem.cs
+++ b/Content.Client/Fax/System/FaxVisualsSystem.cs
@@ -25,24 +25,30 @@ private void OnAppearanceChanged(EntityUid uid, FaxMachineComponent component, r
if (args.Sprite == null)
return;
- if (_appearance.TryGetData(uid, FaxMachineVisuals.VisualState, out FaxMachineVisualState visuals) && visuals == FaxMachineVisualState.Inserting)
+ if (_player.HasRunningAnimation(uid, "faxecute"))
+ return;
+
+ if (_appearance.TryGetData(uid, FaxMachineVisuals.VisualState, out FaxMachineVisualState visuals) &&
+ visuals == FaxMachineVisualState.Inserting)
{
- _player.Play(uid, new Animation()
- {
- Length = TimeSpan.FromSeconds(2.4),
- AnimationTracks =
+ _player.Play(uid,
+ new Animation()
{
- new AnimationTrackSpriteFlick()
+ Length = TimeSpan.FromSeconds(2.4),
+ AnimationTracks =
{
- LayerKey = FaxMachineVisuals.VisualState,
- KeyFrames =
+ new AnimationTrackSpriteFlick()
{
- new AnimationTrackSpriteFlick.KeyFrame(component.InsertingState, 0f),
- new AnimationTrackSpriteFlick.KeyFrame("icon", 2.4f),
- }
- }
- }
- }, "faxecute");
+ LayerKey = FaxMachineVisuals.VisualState,
+ KeyFrames =
+ {
+ new AnimationTrackSpriteFlick.KeyFrame(component.InsertingState, 0f),
+ new AnimationTrackSpriteFlick.KeyFrame("icon", 2.4f),
+ },
+ },
+ },
+ },
+ "faxecute");
}
}
}
diff --git a/Content.Client/Fax/UI/FaxBoundUi.cs b/Content.Client/Fax/UI/FaxBoundUi.cs
index a95066a3b58..ca2e834b4fe 100644
--- a/Content.Client/Fax/UI/FaxBoundUi.cs
+++ b/Content.Client/Fax/UI/FaxBoundUi.cs
@@ -25,10 +25,7 @@ protected override void Open()
{
base.Open();
- _window = new FaxWindow();
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.FileButtonPressed += OnFileButtonPressed;
_window.CopyButtonPressed += OnCopyButtonPressed;
_window.SendButtonPressed += OnSendButtonPressed;
@@ -104,11 +101,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.UpdateState(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- _window?.Dispose();
- }
}
diff --git a/Content.Client/Flash/FlashOverlay.cs b/Content.Client/Flash/FlashOverlay.cs
index 9ea00275e84..046be2aa621 100644
--- a/Content.Client/Flash/FlashOverlay.cs
+++ b/Content.Client/Flash/FlashOverlay.cs
@@ -1,27 +1,22 @@
using Content.Shared.Flash;
using Content.Shared.Flash.Components;
using Content.Shared.StatusEffect;
-using Content.Client.Viewport;
using Robust.Client.Graphics;
-using Robust.Client.State;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
-using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client.Flash
{
public sealed class FlashOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IClyde _displayManager = default!;
- [Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
- private readonly StatusEffectsSystem _statusSys;
+ private readonly StatusEffectsSystem _statusSys;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _shader;
@@ -56,20 +51,6 @@ protected override void FrameUpdate(FrameEventArgs args)
PercentComplete = timeDone / lastsFor;
}
- public void ReceiveFlash()
- {
- if (_stateManager.CurrentState is IMainViewportState state)
- {
- // take a screenshot
- // note that the callback takes a while and ScreenshotTexture will be null the first few Draws
- state.Viewport.Viewport.Screenshot(image =>
- {
- var rgba32Image = image.CloneAs(SixLabors.ImageSharp.Configuration.Default);
- ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
- });
- }
- }
-
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
@@ -82,6 +63,11 @@ protected override bool BeforeDraw(in OverlayDrawArgs args)
protected override void Draw(in OverlayDrawArgs args)
{
+ if (RequestScreenTexture && ScreenTexture != null)
+ {
+ ScreenshotTexture = ScreenTexture;
+ RequestScreenTexture = false; // we only need the first frame, so we can stop the request now for performance reasons
+ }
if (ScreenshotTexture == null)
return;
@@ -96,7 +82,6 @@ protected override void DisposeBehavior()
{
base.DisposeBehavior();
ScreenshotTexture = null;
- PercentComplete = 1.0f;
}
}
}
diff --git a/Content.Client/Flash/FlashSystem.cs b/Content.Client/Flash/FlashSystem.cs
index 9a0579f6aa3..146d84b990f 100644
--- a/Content.Client/Flash/FlashSystem.cs
+++ b/Content.Client/Flash/FlashSystem.cs
@@ -22,7 +22,6 @@ public override void Initialize()
SubscribeLocalEvent(OnShutdown);
SubscribeLocalEvent(OnPlayerAttached);
SubscribeLocalEvent(OnPlayerDetached);
- SubscribeLocalEvent(OnStatusAdded);
_overlay = new();
}
@@ -34,8 +33,8 @@ private void OnPlayerAttached(EntityUid uid, FlashedComponent component, LocalPl
private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args)
{
- _overlay.PercentComplete = 1.0f;
_overlay.ScreenshotTexture = null;
+ _overlay.RequestScreenTexture = false;
_overlayMan.RemoveOverlay(_overlay);
}
@@ -43,6 +42,7 @@ private void OnInit(EntityUid uid, FlashedComponent component, ComponentInit arg
{
if (_player.LocalEntity == uid)
{
+ _overlay.RequestScreenTexture = true;
_overlayMan.AddOverlay(_overlay);
}
}
@@ -51,17 +51,9 @@ private void OnShutdown(EntityUid uid, FlashedComponent component, ComponentShut
{
if (_player.LocalEntity == uid)
{
- _overlay.PercentComplete = 1.0f;
_overlay.ScreenshotTexture = null;
+ _overlay.RequestScreenTexture = false;
_overlayMan.RemoveOverlay(_overlay);
}
}
-
- private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args)
- {
- if (_player.LocalEntity == uid && args.Key == FlashedKey)
- {
- _overlay.ReceiveFlash();
- }
- }
}
diff --git a/Content.Client/Fluids/PuddleOverlay.cs b/Content.Client/Fluids/PuddleOverlay.cs
index a8c1d355105..caa5a925807 100644
--- a/Content.Client/Fluids/PuddleOverlay.cs
+++ b/Content.Client/Fluids/PuddleOverlay.cs
@@ -14,6 +14,7 @@ public sealed class PuddleOverlay : Overlay
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
private readonly PuddleDebugOverlaySystem _debugOverlaySystem;
+ private readonly SharedTransformSystem _transformSystem;
private readonly Color _heavyPuddle = new(0, 255, 255, 50);
private readonly Color _mediumPuddle = new(0, 150, 255, 50);
@@ -29,6 +30,7 @@ public PuddleOverlay()
_debugOverlaySystem = _entitySystemManager.GetEntitySystem();
var cache = IoCManager.Resolve();
_font = new VectorFont(cache.GetResource("/Fonts/NotoSans/NotoSans-Regular.ttf"), 8);
+ _transformSystem = _entityManager.System();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -56,7 +58,7 @@ private void DrawWorld(in OverlayDrawArgs args)
continue;
var gridXform = xformQuery.GetComponent(gridId);
- var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(xformQuery);
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform, xformQuery);
gridBounds = invWorldMatrix.TransformBox(args.WorldBounds).Enlarged(mapGrid.TileSize * 2);
drawHandle.SetTransform(worldMatrix);
@@ -89,7 +91,7 @@ private void DrawScreen(in OverlayDrawArgs args)
continue;
var gridXform = xformQuery.GetComponent(gridId);
- var (_, _, matrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(xformQuery);
+ var (_, _, matrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform, xformQuery);
var gridBounds = invMatrix.TransformBox(args.WorldBounds).Enlarged(mapGrid.TileSize * 2);
foreach (var debugOverlayData in _debugOverlaySystem.GetData(gridId))
diff --git a/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs b/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs
index ba49f11ea0f..08596b04e6e 100644
--- a/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs
+++ b/Content.Client/Forensics/ForensicScannerBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Robust.Client.GameObjects;
using Robust.Shared.Timing;
using Content.Shared.Forensics;
+using Robust.Client.UserInterface;
namespace Content.Client.Forensics
{
@@ -21,11 +22,9 @@ public ForensicScannerBoundUserInterface(EntityUid owner, Enum uiKey) : base(own
protected override void Open()
{
base.Open();
- _window = new ForensicScannerMenu();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.Print.OnPressed += _ => Print();
_window.Clear.OnPressed += _ => Clear();
- _window.OpenCentered();
}
private void Print()
@@ -62,6 +61,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_printCooldown = cast.PrintCooldown;
+ // TODO: Fix this
if (cast.PrintReadyAt > _gameTiming.CurTime)
Timer.Spawn(cast.PrintReadyAt - _gameTiming.CurTime, () =>
{
@@ -71,14 +71,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.UpdateState(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs
index 5f7f8e0056c..dd013ed2357 100644
--- a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs
+++ b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs
@@ -54,10 +54,16 @@ public void UpdateState(ForensicScannerBoundUserInterfaceState msg)
}
text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
- foreach (var dna in msg.DNAs)
+ foreach (var dna in msg.TouchDNAs)
{
text.AppendLine(dna);
}
+ foreach (var dna in msg.SolutionDNAs)
+ {
+ if (msg.TouchDNAs.Contains(dna))
+ continue;
+ text.AppendLine(dna);
+ }
text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
foreach (var residue in msg.Residues)
diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs
index 6236cd8e958..1e6fd485b38 100644
--- a/Content.Client/Gameplay/GameplayStateBase.cs
+++ b/Content.Client/Gameplay/GameplayStateBase.cs
@@ -2,6 +2,7 @@
using System.Numerics;
using Content.Client.Clickable;
using Content.Client.UserInterface;
+using Content.Client.Viewport;
using Content.Shared.Input;
using Robust.Client.ComponentTrees;
using Robust.Client.GameObjects;
@@ -13,11 +14,13 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Console;
+using Robust.Shared.Graphics;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Timing;
+using YamlDotNet.Serialization.TypeInspectors;
namespace Content.Client.Gameplay
{
@@ -98,17 +101,40 @@ private bool HandleInspect(ICommonSession? session, EntityCoordinates coords, En
public EntityUid? GetClickedEntity(MapCoordinates coordinates)
{
- var first = GetClickableEntities(coordinates).FirstOrDefault();
+ return GetClickedEntity(coordinates, _eyeManager.CurrentEye);
+ }
+
+ public EntityUid? GetClickedEntity(MapCoordinates coordinates, IEye? eye)
+ {
+ if (eye == null)
+ return null;
+
+ var first = GetClickableEntities(coordinates, eye).FirstOrDefault();
return first.IsValid() ? first : null;
}
public IEnumerable GetClickableEntities(EntityCoordinates coordinates)
{
- return GetClickableEntities(coordinates.ToMap(_entityManager, _entitySystemManager.GetEntitySystem()));
+ var transformSystem = _entitySystemManager.GetEntitySystem();
+ return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates));
}
public IEnumerable GetClickableEntities(MapCoordinates coordinates)
{
+ return GetClickableEntities(coordinates, _eyeManager.CurrentEye);
+ }
+
+ public IEnumerable GetClickableEntities(MapCoordinates coordinates, IEye? eye)
+ {
+ /*
+ * TODO:
+ * 1. Stuff like MeleeWeaponSystem need an easy way to hook into viewport specific entities / entities under mouse
+ * 2. Cleanup the mess around InteractionOutlineSystem + below the keybind click detection.
+ */
+
+ if (eye == null)
+ return Array.Empty();
+
// Find all the entities intersecting our click
var spriteTree = _entityManager.EntitySysManager.GetEntitySystem();
var entities = spriteTree.QueryAabb(coordinates.MapId, Box2.CenteredAround(coordinates.Position, new Vector2(1, 1)));
@@ -116,15 +142,12 @@ public IEnumerable GetClickableEntities(MapCoordinates coordinates)
// Check the entities against whether or not we can click them
var foundEntities = new List<(EntityUid, int, uint, float)>(entities.Count);
var clickQuery = _entityManager.GetEntityQuery();
- var xformQuery = _entityManager.GetEntityQuery();
-
- // TODO: Smelly
- var eye = _eyeManager.CurrentEye;
+ var clickables = _entityManager.System();
foreach (var entity in entities)
{
if (clickQuery.TryGetComponent(entity.Uid, out var component) &&
- component.CheckClick(entity.Component, entity.Transform, xformQuery, coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
+ clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
{
foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom));
}
@@ -184,15 +207,27 @@ protected virtual void OnKeyBindStateChanged(ViewportBoundKeyEventArgs args)
EntityCoordinates coordinates = default;
EntityUid? entityToClick = null;
- if (args.Viewport is IViewportControl vp)
+ if (args.Viewport is IViewportControl vp && kArgs.PointerLocation.IsValid)
{
var mousePosWorld = vp.PixelToMap(kArgs.PointerLocation.Position);
- entityToClick = GetClickedEntity(mousePosWorld);
+
+ if (vp is ScalingViewport svp)
+ {
+ entityToClick = GetClickedEntity(mousePosWorld, svp.Eye);
+ }
+ else
+ {
+ entityToClick = GetClickedEntity(mousePosWorld);
+ }
coordinates = _mapManager.TryFindGridAt(mousePosWorld, out _, out var grid) ?
grid.MapToGrid(mousePosWorld) :
EntityCoordinates.FromMap(_mapManager, mousePosWorld);
}
+ else
+ {
+ coordinates = EntityCoordinates.Invalid;
+ }
var message = new ClientFullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId)
{
diff --git a/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs b/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs
index fdb3cdbc010..457b70ca7ca 100644
--- a/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs
+++ b/Content.Client/Gateway/UI/GatewayBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Gateway;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Gateway.UI;
@@ -17,24 +18,13 @@ protected override void Open()
{
base.Open();
- _window = new GatewayWindow(EntMan.GetNetEntity(Owner));
+ _window = this.CreateWindow();
+ _window.SetEntity(EntMan.GetNetEntity(Owner));
_window.OpenPortal += destination =>
{
SendMessage(new GatewayOpenPortalMessage(destination));
};
- _window.OnClose += Close;
- _window?.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- {
- _window?.Dispose();
- _window = null;
- }
}
protected override void UpdateState(BoundUserInterfaceState state)
diff --git a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs
index 889dd6e1759..1c779b2b350 100644
--- a/Content.Client/Gateway/UI/GatewayWindow.xaml.cs
+++ b/Content.Client/Gateway/UI/GatewayWindow.xaml.cs
@@ -22,7 +22,7 @@ public sealed partial class GatewayWindow : FancyWindow,
public event Action? OpenPortal;
private List _destinations = new();
- public readonly NetEntity Owner;
+ public NetEntity Owner;
private NetEntity? _current;
private TimeSpan _nextReady;
@@ -46,16 +46,20 @@ public sealed partial class GatewayWindow : FancyWindow,
///
private bool _isCooldownPending = true;
- public GatewayWindow(NetEntity netEntity)
+ public GatewayWindow()
{
RobustXamlLoader.Load(this);
var dependencies = IoCManager.Instance!;
_timing = dependencies.Resolve();
- Owner = netEntity;
NextUnlockBar.ForegroundStyleBoxOverride = new StyleBoxFlat(Color.FromHex("#C74EBD"));
}
+ public void SetEntity(NetEntity entity)
+ {
+
+ }
+
public void UpdateState(GatewayBoundUserInterfaceState state)
{
_destinations = state.Destinations;
diff --git a/Content.Client/Ghost/GhostRoleRadioBoundUserInterface.cs b/Content.Client/Ghost/GhostRoleRadioBoundUserInterface.cs
new file mode 100644
index 00000000000..52ea835f4a8
--- /dev/null
+++ b/Content.Client/Ghost/GhostRoleRadioBoundUserInterface.cs
@@ -0,0 +1,29 @@
+using Content.Shared.Ghost.Roles;
+using Robust.Client.UserInterface;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Ghost;
+
+public sealed class GhostRoleRadioBoundUserInterface : BoundUserInterface
+{
+ private GhostRoleRadioMenu? _ghostRoleRadioMenu;
+
+ public GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ IoCManager.InjectDependencies(this);
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _ghostRoleRadioMenu = this.CreateWindow();
+ _ghostRoleRadioMenu.SetEntity(Owner);
+ _ghostRoleRadioMenu.SendGhostRoleRadioMessageAction += SendGhostRoleRadioMessage;
+ }
+
+ private void SendGhostRoleRadioMessage(ProtoId protoId)
+ {
+ SendMessage(new GhostRoleRadioMessage(protoId));
+ }
+}
diff --git a/Content.Client/Ghost/GhostRoleRadioMenu.xaml b/Content.Client/Ghost/GhostRoleRadioMenu.xaml
new file mode 100644
index 00000000000..c35ee128c52
--- /dev/null
+++ b/Content.Client/Ghost/GhostRoleRadioMenu.xaml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/Content.Client/Ghost/GhostRoleRadioMenu.xaml.cs b/Content.Client/Ghost/GhostRoleRadioMenu.xaml.cs
new file mode 100644
index 00000000000..3897b1b949c
--- /dev/null
+++ b/Content.Client/Ghost/GhostRoleRadioMenu.xaml.cs
@@ -0,0 +1,106 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Ghost.Roles;
+using Content.Shared.Ghost.Roles.Components;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using System.Numerics;
+
+namespace Content.Client.Ghost;
+
+public sealed partial class GhostRoleRadioMenu : RadialMenu
+{
+ [Dependency] private readonly EntityManager _entityManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ public event Action>? SendGhostRoleRadioMessageAction;
+
+ public EntityUid Entity { get; set; }
+
+ public GhostRoleRadioMenu()
+ {
+ IoCManager.InjectDependencies(this);
+ RobustXamlLoader.Load(this);
+ }
+
+ public void SetEntity(EntityUid uid)
+ {
+ Entity = uid;
+ RefreshUI();
+ }
+
+ private void RefreshUI()
+ {
+ // The main control that will contain all the clickable options
+ var main = FindControl("Main");
+
+ // The purpose of this radial UI is for ghost role radios that allow you to select
+ // more than one potential option, such as with kobolds/lizards.
+ // This means that it won't show anything if SelectablePrototypes is empty.
+ if (!_entityManager.TryGetComponent(Entity, out var comp))
+ return;
+
+ foreach (var ghostRoleProtoString in comp.SelectablePrototypes)
+ {
+ // For each prototype we find we want to create a button that uses the name of the ghost role
+ // as the hover tooltip, and the icon is taken from either the ghost role entityprototype
+ // or the indicated icon entityprototype.
+ if (!_prototypeManager.TryIndex(ghostRoleProtoString, out var ghostRoleProto))
+ continue;
+
+ var button = new GhostRoleRadioMenuButton()
+ {
+ StyleClasses = { "RadialMenuButton" },
+ SetSize = new Vector2(64, 64),
+ ToolTip = Loc.GetString(ghostRoleProto.Name),
+ ProtoId = ghostRoleProto.ID,
+ };
+
+ var entProtoView = new EntityPrototypeView()
+ {
+ SetSize = new Vector2(48, 48),
+ VerticalAlignment = VAlignment.Center,
+ HorizontalAlignment = HAlignment.Center,
+ Stretch = SpriteView.StretchMode.Fill
+ };
+
+ // pick the icon if it exists, otherwise fallback to the ghost role's entity
+ if (_prototypeManager.TryIndex(ghostRoleProto.IconPrototype, out var iconProto))
+ entProtoView.SetPrototype(iconProto);
+ else
+ entProtoView.SetPrototype(ghostRoleProto.EntityPrototype);
+
+ button.AddChild(entProtoView);
+ main.AddChild(button);
+ AddGhostRoleRadioMenuButtonOnClickActions(main);
+ }
+ }
+
+ private void AddGhostRoleRadioMenuButtonOnClickActions(Control control)
+ {
+ var mainControl = control as RadialContainer;
+
+ if (mainControl == null)
+ return;
+
+ foreach (var child in mainControl.Children)
+ {
+ var castChild = child as GhostRoleRadioMenuButton;
+
+ if (castChild == null)
+ continue;
+
+ castChild.OnButtonUp += _ =>
+ {
+ SendGhostRoleRadioMessageAction?.Invoke(castChild.ProtoId);
+ Close();
+ };
+ }
+ }
+}
+
+public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButton
+{
+ public ProtoId ProtoId { get; set; }
+}
diff --git a/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs b/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs
index d72da3e8120..32b40747d55 100644
--- a/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs
+++ b/Content.Client/Gravity/UI/GravityGeneratorBoundUserInterface.cs
@@ -1,6 +1,6 @@
using Content.Shared.Gravity;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Gravity.UI
{
@@ -18,17 +18,8 @@ protected override void Open()
{
base.Open();
- _window = new GravityGeneratorWindow(this);
-
- /*
- _window.Switch.OnPressed += _ =>
- {
- SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(!IsOn));
- };
- */
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.SetEntity(Owner);
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -39,14 +30,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(castState);
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
-
- _window?.Dispose();
- }
-
public void SetPowerSwitch(bool on)
{
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on));
diff --git a/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs b/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs
index 75f8eb479b5..6f04133b594 100644
--- a/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs
+++ b/Content.Client/Gravity/UI/GravityGeneratorWindow.xaml.cs
@@ -12,22 +12,23 @@ public sealed partial class GravityGeneratorWindow : FancyWindow
{
private readonly ButtonGroup _buttonGroup = new();
- private readonly GravityGeneratorBoundUserInterface _owner;
+ public event Action? OnPowerSwitch;
- public GravityGeneratorWindow(GravityGeneratorBoundUserInterface owner)
+ public GravityGeneratorWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- _owner = owner;
-
OnButton.Group = _buttonGroup;
OffButton.Group = _buttonGroup;
- OnButton.OnPressed += _ => _owner.SetPowerSwitch(true);
- OffButton.OnPressed += _ => _owner.SetPowerSwitch(false);
+ OnButton.OnPressed += _ => OnPowerSwitch?.Invoke(true);
+ OffButton.OnPressed += _ => OnPowerSwitch?.Invoke(false);
+ }
- EntityView.SetEntity(owner.Owner);
+ public void SetEntity(EntityUid uid)
+ {
+ EntityView.SetEntity(uid);
}
public void UpdateState(SharedGravityGeneratorComponent.GeneratorState state)
diff --git a/Content.Client/Guidebook/Controls/GuidebookError.xaml b/Content.Client/Guidebook/Controls/GuidebookError.xaml
new file mode 100644
index 00000000000..b84d527ea0a
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuidebookError.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Guidebook/Controls/GuidebookError.xaml.cs b/Content.Client/Guidebook/Controls/GuidebookError.xaml.cs
new file mode 100644
index 00000000000..461f196c838
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuidebookError.xaml.cs
@@ -0,0 +1,23 @@
+using JetBrains.Annotations;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Guidebook.Controls;
+
+[UsedImplicitly] [GenerateTypedNameReferences]
+public sealed partial class GuidebookError : BoxContainer
+{
+ public GuidebookError()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public GuidebookError(string original, string? error) : this()
+ {
+ Original.AddText(original);
+
+ if (error is not null)
+ Error.AddText(error);
+ }
+}
diff --git a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
index f5ee1200cc6..469b0ed2224 100644
--- a/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
+++ b/Content.Client/Guidebook/Controls/GuidebookWindow.xaml.cs
@@ -16,15 +16,18 @@ namespace Content.Client.Guidebook.Controls;
[GenerateTypedNameReferences]
public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
{
- [Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly DocumentParsingManager _parsingMan = default!;
+ [Dependency] private readonly IResourceManager _resourceManager = default!;
private Dictionary, GuideEntry> _entries = new();
+ private readonly ISawmill _sawmill;
+
public GuidebookWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ _sawmill = Logger.GetSawmill("Guidebook");
Tree.OnSelectedItemChanged += OnSelectionChanged;
@@ -34,6 +37,20 @@ public GuidebookWindow()
};
}
+ public void HandleClick(string link)
+ {
+ if (!_entries.TryGetValue(link, out var entry))
+ return;
+
+ if (Tree.TryGetIndexFromMetadata(entry, out var index))
+ {
+ Tree.ExpandParentEntries(index.Value);
+ Tree.SetSelectedIndex(index);
+ }
+ else
+ ShowGuide(entry);
+ }
+
private void OnSelectionChanged(TreeItem? item)
{
if (item != null && item.Metadata is GuideEntry entry)
@@ -69,8 +86,9 @@ private void ShowGuide(GuideEntry entry)
if (!_parsingMan.TryAddMarkup(EntryContainer, file.ReadToEnd()))
{
- EntryContainer.AddChild(new Label() { Text = "ERROR: Failed to parse document." });
- Logger.Error($"Failed to parse contents of guide document {entry.Id}.");
+ // The guidebook will automatically display the in-guidebook error if it fails
+
+ _sawmill.Error($"Failed to parse contents of guide document {entry.Id}.");
}
}
@@ -122,8 +140,10 @@ private IEnumerable GetSortedEntries(List GetSortedEntries(List Loc.GetString(rootEntry.Name));
}
- private void RepopulateTree(List>? roots = null, ProtoId? forcedRoot = null)
+ private void RepopulateTree(List>? roots = null,
+ ProtoId? forcedRoot = null)
{
Tree.Clear();
HashSet> addedEntries = new();
- TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
+ var parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
foreach (var entry in GetSortedEntries(roots))
{
AddEntry(entry.Id, parent, addedEntries);
}
+
Tree.SetAllExpanded(true);
}
- private TreeItem? AddEntry(ProtoId id, TreeItem? parent, HashSet> addedEntries)
+ private TreeItem? AddEntry(ProtoId id,
+ TreeItem? parent,
+ HashSet> addedEntries)
{
if (!_entries.TryGetValue(id, out var entry))
return null;
@@ -177,22 +201,6 @@ private void RepopulateTree(List>? roots = null, Pr
return item;
}
- public void HandleClick(string link)
- {
- if (!_entries.TryGetValue(link, out var entry))
- return;
-
- if (Tree.TryGetIndexFromMetadata(entry, out var index))
- {
- Tree.ExpandParentEntries(index.Value);
- Tree.SetSelectedIndex(index);
- }
- else
- {
- ShowGuide(entry);
- }
- }
-
private void HandleFilter()
{
var emptySearch = SearchBar.Text.Trim().Length == 0;
@@ -206,6 +214,5 @@ private void HandleFilter()
element.SetHiddenState(true, SearchBar.Text.Trim());
}
}
-
}
}
diff --git a/Content.Client/Guidebook/DocumentParsingManager.cs b/Content.Client/Guidebook/DocumentParsingManager.cs
index e8a0743b9e0..857ae552024 100644
--- a/Content.Client/Guidebook/DocumentParsingManager.cs
+++ b/Content.Client/Guidebook/DocumentParsingManager.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Content.Client.Guidebook.Controls;
using Content.Client.Guidebook.Richtext;
using Content.Shared.Guidebook;
using Pidgin;
@@ -7,6 +8,7 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Sandboxing;
+using Robust.Shared.Utility;
using static Pidgin.Parser;
namespace Content.Client.Guidebook;
@@ -22,8 +24,10 @@ public sealed partial class DocumentParsingManager
[Dependency] private readonly ISandboxHelper _sandboxHelper = default!;
private readonly Dictionary> _tagControlParsers = new();
- private Parser _tagParser = default!;
private Parser _controlParser = default!;
+
+ private ISawmill _sawmill = default!;
+ private Parser _tagParser = default!;
public Parser> ControlParser = default!;
public void Initialize()
@@ -32,7 +36,8 @@ public void Initialize()
.Assert(_tagControlParsers.ContainsKey, tag => $"unknown tag: {tag}")
.Bind(tag => _tagControlParsers[tag]);
- _controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser).Before(SkipWhitespaces);
+ _controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser)
+ .Before(SkipWhitespaces);
foreach (var typ in _reflectionManager.GetAllChildren())
{
@@ -40,6 +45,8 @@ public void Initialize()
}
ControlParser = SkipWhitespaces.Then(_controlParser.Many());
+
+ _sawmill = Logger.GetSawmill("Guidebook");
}
public bool TryAddMarkup(Control control, ProtoId entryId, bool log = true)
@@ -68,37 +75,57 @@ public bool TryAddMarkup(Control control, string text, bool log = true)
}
catch (Exception e)
{
- if (log)
- Logger.Error($"Encountered error while generating markup controls: {e}");
+ _sawmill.Error($"Encountered error while generating markup controls: {e}");
+
+ control.AddChild(new GuidebookError(text, e.ToStringBetter()));
+
return false;
}
return true;
}
- private Parser CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox) => Map(
- (args, controls) =>
- {
- var tag = (IDocumentTag) sandbox.CreateInstance(tagType);
- if (!tag.TryParseTag(args, out var control))
- {
- Logger.Error($"Failed to parse {tagId} args");
- return new Control();
- }
+ private Parser CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox)
+ {
+ return Map(
+ (args, controls) =>
+ {
+ try
+ {
+ var tag = (IDocumentTag) sandbox.CreateInstance(tagType);
+ if (!tag.TryParseTag(args, out var control))
+ {
+ _sawmill.Error($"Failed to parse {tagId} args");
+ return new GuidebookError(args.ToString() ?? tagId, $"Failed to parse {tagId} args");
+ }
- foreach (var child in controls)
- {
- control.AddChild(child);
- }
- return control;
- },
- ParseTagArgs(tagId),
- TagContentParser(tagId)).Labelled($"{tagId} control");
+ foreach (var child in controls)
+ {
+ control.AddChild(child);
+ }
+
+ return control;
+ }
+ catch (Exception e)
+ {
+ var output = args.Aggregate(string.Empty,
+ (current, pair) => current + $"{pair.Key}=\"{pair.Value}\" ");
+
+ _sawmill.Error($"Tag: {tagId} \n Arguments: {output}/>");
+ return new GuidebookError($"Tag: {tagId}\nArguments: {output}", e.ToString());
+ }
+ },
+ ParseTagArgs(tagId),
+ TagContentParser(tagId))
+ .Labelled($"{tagId} control");
+ }
// Parse a bunch of controls until we encounter a matching closing tag.
- private Parser> TagContentParser(string tag) =>
- OneOf(
- Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty()),
- TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
- );
+ private Parser> TagContentParser(string tag)
+ {
+ return OneOf(
+ Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty()),
+ TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
+ );
+ }
}
diff --git a/Content.Client/Guidebook/DocumentParsingManager.static.cs b/Content.Client/Guidebook/DocumentParsingManager.static.cs
index ab38fcb1546..5d25d8f6452 100644
--- a/Content.Client/Guidebook/DocumentParsingManager.static.cs
+++ b/Content.Client/Guidebook/DocumentParsingManager.static.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Content.Client.Guidebook.Controls;
using Pidgin;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -14,92 +15,142 @@ public sealed partial class DocumentParsingManager
{
private const string ListBullet = " › ";
- #region Text Parsing
- #region Basic Text Parsing
- // Try look for an escaped character. If found, skip the escaping slash and return the character.
- private static readonly Parser TryEscapedChar = Try(Char('\\').Then(OneOf(
- Try(Char('<')),
- Try(Char('>')),
- Try(Char('\\')),
- Try(Char('-')),
- Try(Char('=')),
- Try(Char('"')),
- Try(Char(' ')),
- Try(Char('n')).ThenReturn('\n'),
- Try(Char('t')).ThenReturn('\t')
- )));
+ // Parser that consumes a - and then just parses normal rich text with some prefix text (a bullet point).
+ private static readonly Parser TryEscapedChar = Try(Char('\\')
+ .Then(OneOf(
+ Try(Char('<')),
+ Try(Char('>')),
+ Try(Char('\\')),
+ Try(Char('-')),
+ Try(Char('=')),
+ Try(Char('"')),
+ Try(Char(' ')),
+ Try(Char('n')).ThenReturn('\n'),
+ Try(Char('t')).ThenReturn('\t')
+ )));
private static readonly Parser SkipNewline = Whitespace.SkipUntil(Char('\n'));
- private static readonly Parser TrySingleNewlineToSpace = Try(SkipNewline).Then(SkipWhitespaces).ThenReturn(' ');
+ private static readonly Parser TrySingleNewlineToSpace =
+ Try(SkipNewline).Then(SkipWhitespaces).ThenReturn(' ');
private static readonly Parser TextChar = OneOf(
TryEscapedChar, // consume any backslashed being used to escape text
TrySingleNewlineToSpace, // turn single newlines into spaces
Any // just return the character.
- );
+ );
- // like TextChar, but not skipping whitespace around newlines
private static readonly Parser QuotedTextChar = OneOf(TryEscapedChar, Any);
+ private static readonly Parser QuotedText =
+ Char('"').Then(QuotedTextChar.Until(Try(Char('"'))).Select(string.Concat)).Labelled("quoted text");
+
+ private static readonly Parser TryStartList =
+ Try(SkipNewline.Then(SkipWhitespaces).Then(Char('-'))).Then(SkipWhitespaces);
+
+ private static readonly Parser TryStartTag = Try(Char('<')).Then(SkipWhitespaces);
+
+ private static readonly Parser TryStartParagraph =
+ Try(SkipNewline.Then(SkipNewline)).Then(SkipWhitespaces);
+
+ private static readonly Parser TryLookTextEnd =
+ Lookahead(OneOf(TryStartTag, TryStartList, TryStartParagraph, Try(Whitespace.SkipUntil(End))));
+
+ private static readonly Parser TextParser =
+ TextChar.AtLeastOnceUntil(TryLookTextEnd).Select(string.Concat);
+
+ private static readonly Parser TextControlParser = Try(Map(text =>
+ {
+ var rt = new RichTextLabel
+ {
+ HorizontalExpand = true,
+ Margin = new Thickness(0, 0, 0, 15.0f)
+ };
+
+ var msg = new FormattedMessage();
+ // THANK YOU RICHTEXT VERY COOL
+ // (text doesn't default to white).
+ msg.PushColor(Color.White);
+
+ // If the parsing fails, don't throw an error and instead make an inline error message
+ string? error;
+ if (!msg.TryAddMarkup(text, out error))
+ {
+ Logger.GetSawmill("Guidebook").Error("Failed to parse RichText in Guidebook");
+
+ return new GuidebookError(text, error);
+ }
+
+ msg.Pop();
+ rt.SetMessage(msg);
+ return rt;
+ },
+ TextParser)
+ .Cast())
+ .Labelled("richtext");
+
+ private static readonly Parser HeaderControlParser = Try(Char('#'))
+ .Then(SkipWhitespaces.Then(Map(text => new Label
+ {
+ Text = text,
+ StyleClasses = { "LabelHeadingBigger" }
+ },
+ AnyCharExcept('\n').AtLeastOnceString())
+ .Cast()))
+ .Labelled("header");
+
+ private static readonly Parser SubHeaderControlParser = Try(String("##"))
+ .Then(SkipWhitespaces.Then(Map(text => new Label
+ {
+ Text = text,
+ StyleClasses = { "LabelHeading" }
+ },
+ AnyCharExcept('\n').AtLeastOnceString())
+ .Cast()))
+ .Labelled("subheader");
+
+ private static readonly Parser TryHeaderControl = OneOf(SubHeaderControlParser, HeaderControlParser);
+
+ private static readonly Parser ListControlParser = Try(Char('-'))
+ .Then(SkipWhitespaces)
+ .Then(Map(
+ control => new BoxContainer
+ {
+ Children = { new Label { Text = ListBullet, VerticalAlignment = VAlignment.Top }, control },
+ Orientation = LayoutOrientation.Horizontal
+ },
+ TextControlParser)
+ .Cast())
+ .Labelled("list");
+
+ #region Text Parsing
+
+ #region Basic Text Parsing
+
+ // Try look for an escaped character. If found, skip the escaping slash and return the character.
+
+
+ // like TextChar, but not skipping whitespace around newlines
+
+
// Quoted text
- private static readonly Parser QuotedText = Char('"').Then(QuotedTextChar.Until(Try(Char('"'))).Select(string.Concat)).Labelled("quoted text");
+
#endregion
#region rich text-end markers
- private static readonly Parser TryStartList = Try(SkipNewline.Then(SkipWhitespaces).Then(Char('-'))).Then(SkipWhitespaces);
- private static readonly Parser TryStartTag = Try(Char('<')).Then(SkipWhitespaces);
- private static readonly Parser TryStartParagraph = Try(SkipNewline.Then(SkipNewline)).Then(SkipWhitespaces);
- private static readonly Parser TryLookTextEnd = Lookahead(OneOf(TryStartTag, TryStartList, TryStartParagraph, Try(Whitespace.SkipUntil(End))));
+
#endregion
// parses text characters until it hits a text-end
- private static readonly Parser TextParser = TextChar.AtLeastOnceUntil(TryLookTextEnd).Select(string.Concat);
- private static readonly Parser TextControlParser = Try(Map(text =>
- {
- var rt = new RichTextLabel()
- {
- HorizontalExpand = true,
- Margin = new Thickness(0, 0, 0, 15.0f),
- };
-
- var msg = new FormattedMessage();
- // THANK YOU RICHTEXT VERY COOL
- // (text doesn't default to white).
- msg.PushColor(Color.White);
- msg.AddMarkup(text);
- msg.Pop();
- rt.SetMessage(msg);
- return rt;
- }, TextParser).Cast()).Labelled("richtext");
#endregion
#region Headers
- private static readonly Parser HeaderControlParser = Try(Char('#')).Then(SkipWhitespaces.Then(Map(text => new Label()
- {
- Text = text,
- StyleClasses = { "LabelHeadingBigger" }
- }, AnyCharExcept('\n').AtLeastOnceString()).Cast())).Labelled("header");
- private static readonly Parser SubHeaderControlParser = Try(String("##")).Then(SkipWhitespaces.Then(Map(text => new Label()
- {
- Text = text,
- StyleClasses = { "LabelHeading" }
- }, AnyCharExcept('\n').AtLeastOnceString()).Cast())).Labelled("subheader");
-
- private static readonly Parser TryHeaderControl = OneOf(SubHeaderControlParser, HeaderControlParser);
#endregion
- // Parser that consumes a - and then just parses normal rich text with some prefix text (a bullet point).
- private static readonly Parser ListControlParser = Try(Char('-')).Then(SkipWhitespaces).Then(Map(
- control => new BoxContainer()
- {
- Children = { new Label() { Text = ListBullet, VerticalAlignment = VAlignment.Top, }, control },
- Orientation = LayoutOrientation.Horizontal,
- }, TextControlParser).Cast()).Labelled("list");
-
#region Tag Parsing
+
// closing brackets for tags
private static readonly Parser TagEnd = Char('>').Then(SkipWhitespaces);
private static readonly Parser ImmediateTagEnd = String("/>").Then(SkipWhitespaces);
@@ -107,20 +158,24 @@ public sealed partial class DocumentParsingManager
private static readonly Parser TryLookTagEnd = Lookahead(OneOf(Try(TagEnd), Try(ImmediateTagEnd)));
//parse tag argument key. any normal text character up until we hit a "="
- private static readonly Parser TagArgKey = LetterOrDigit.Until(Char('=')).Select(string.Concat).Labelled("tag argument key");
+ private static readonly Parser TagArgKey =
+ LetterOrDigit.Until(Char('=')).Select(string.Concat).Labelled("tag argument key");
// parser for a singular tag argument. Note that each TryQuoteOrChar will consume a whole quoted block before the Until() looks for whitespace
- private static readonly Parser TagArgParser = Map((key, value) => (key, value), TagArgKey, QuotedText).Before(SkipWhitespaces);
+ private static readonly Parser TagArgParser =
+ Map((key, value) => (key, value), TagArgKey, QuotedText).Before(SkipWhitespaces);
// parser for all tag arguments
- private static readonly Parser> TagArgsParser = TagArgParser.Until(TryLookTagEnd);
+ private static readonly Parser> TagArgsParser =
+ TagArgParser.Until(TryLookTagEnd);
// parser for an opening tag.
private static readonly Parser TryOpeningTag =
Try(Char('<'))
- .Then(SkipWhitespaces)
- .Then(TextChar.Until(OneOf(Whitespace.SkipAtLeastOnce(), TryLookTagEnd)))
- .Select(string.Concat).Labelled($"opening tag");
+ .Then(SkipWhitespaces)
+ .Then(TextChar.Until(OneOf(Whitespace.SkipAtLeastOnce(), TryLookTagEnd)))
+ .Select(string.Concat)
+ .Labelled("opening tag");
private static Parser> ParseTagArgs(string tag)
{
@@ -138,5 +193,6 @@ private static Parser TryTagTerminator(string tag)
.Then(TagEnd)
.Labelled($"closing {tag} tag");
}
+
#endregion
}
diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs
index 7319b97b42b..ffa6dfd29d6 100644
--- a/Content.Client/Hands/Systems/HandsSystem.cs
+++ b/Content.Client/Hands/Systems/HandsSystem.cs
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using Content.Client.DisplacementMap;
using Content.Client.Examine;
using Content.Client.Strip;
using Content.Client.Verbs.UI;
@@ -28,6 +29,7 @@ public sealed class HandsSystem : SharedHandsSystem
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly StrippableSystem _stripSys = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
+ [Dependency] private readonly DisplacementMapSystem _displacement = default!;
public event Action? OnPlayerAddHand;
public event Action? OnPlayerRemoveHand;
@@ -345,6 +347,10 @@ private void UpdateHandVisuals(EntityUid uid, EntityUid held, Hand hand, HandsCo
}
sprite.LayerSetData(index, layerData);
+
+ //Add displacement maps
+ if (handComp.HandDisplacement is not null)
+ _displacement.TryAddDisplacement(handComp.HandDisplacement, sprite, index, key, revealedLayers);
}
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs
index dc0a3e9fccd..38760f4aa3c 100644
--- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs
+++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs
@@ -1,6 +1,6 @@
using Content.Shared.MedicalScanner;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.HealthAnalyzer.UI
{
@@ -17,12 +17,9 @@ public HealthAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne
protected override void Open()
{
base.Open();
- _window = new HealthAnalyzerWindow
- {
- Title = EntMan.GetComponent(Owner).EntityName,
- };
- _window.OnClose += Close;
- _window.OpenCentered();
+ _window = this.CreateWindow();
+
+ _window.Title = EntMan.GetComponent(Owner).EntityName;
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
@@ -35,17 +32,5 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
_window.Populate(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- if (_window != null)
- _window.OnClose -= Close;
-
- _window?.Dispose();
- }
}
}
diff --git a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs
index a8872604a4c..53977eb636b 100644
--- a/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs
+++ b/Content.Client/Humanoid/HumanoidMarkingModifierBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
+using Robust.Client.UserInterface;
namespace Content.Client.Humanoid;
@@ -20,8 +21,7 @@ protected override void Open()
{
base.Open();
- _window = new();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.OnMarkingAdded += SendMarkingSet;
_window.OnMarkingRemoved += SendMarkingSet;
_window.OnMarkingColorChange += SendMarkingSetNoResend;
diff --git a/Content.Client/IconSmoothing/ClientRandomIconSmoothSystem.cs b/Content.Client/IconSmoothing/ClientRandomIconSmoothSystem.cs
new file mode 100644
index 00000000000..73db9e1ab95
--- /dev/null
+++ b/Content.Client/IconSmoothing/ClientRandomIconSmoothSystem.cs
@@ -0,0 +1,29 @@
+using Content.Shared.IconSmoothing;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.IconSmoothing;
+
+public sealed class ClientRandomIconSmoothSystem : SharedRandomIconSmoothSystem
+{
+ [Dependency] private readonly IconSmoothSystem _iconSmooth = default!;
+ [Dependency] private readonly AppearanceSystem _appearance = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnAppearanceChange);
+ }
+
+ private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args)
+ {
+ if (!TryComp(ent, out var smooth))
+ return;
+
+ if (!_appearance.TryGetData(ent, RandomIconSmoothState.State, out var state, args.Component))
+ return;
+
+ smooth.StateBase = state;
+ _iconSmooth.SetStateBase(ent, smooth, state);
+ }
+}
diff --git a/Content.Client/IconSmoothing/IconSmoothComponent.cs b/Content.Client/IconSmoothing/IconSmoothComponent.cs
index 88b1f613cb7..040198529c7 100644
--- a/Content.Client/IconSmoothing/IconSmoothComponent.cs
+++ b/Content.Client/IconSmoothing/IconSmoothComponent.cs
@@ -30,7 +30,7 @@ public sealed partial class IconSmoothComponent : Component
/// Prepended to the RSI state.
///
[ViewVariables(VVAccess.ReadWrite), DataField("base")]
- public string StateBase { get; private set; } = string.Empty;
+ public string StateBase { get; set; } = string.Empty;
[DataField("shader", customTypeSerializer:typeof(PrototypeIdSerializer))]
public string? Shader;
diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs
index 4b025608465..11ca75bc824 100644
--- a/Content.Client/IconSmoothing/IconSmoothSystem.cs
+++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs
@@ -55,6 +55,33 @@ private void OnStartup(EntityUid uid, IconSmoothComponent component, ComponentSt
if (component.Mode != IconSmoothingMode.Corners || !TryComp(uid, out SpriteComponent? sprite))
return;
+ SetCornerLayers(sprite, component);
+
+ if (component.Shader != null)
+ {
+ sprite.LayerSetShader(CornerLayers.SE, component.Shader);
+ sprite.LayerSetShader(CornerLayers.NE, component.Shader);
+ sprite.LayerSetShader(CornerLayers.NW, component.Shader);
+ sprite.LayerSetShader(CornerLayers.SW, component.Shader);
+ }
+ }
+
+ public void SetStateBase(EntityUid uid, IconSmoothComponent component, string newState)
+ {
+ if (!TryComp(uid, out var sprite))
+ return;
+
+ component.StateBase = newState;
+ SetCornerLayers(sprite, component);
+ }
+
+ private void SetCornerLayers(SpriteComponent sprite, IconSmoothComponent component)
+ {
+ sprite.LayerMapRemove(CornerLayers.SE);
+ sprite.LayerMapRemove(CornerLayers.NE);
+ sprite.LayerMapRemove(CornerLayers.NW);
+ sprite.LayerMapRemove(CornerLayers.SW);
+
var state0 = $"{component.StateBase}0";
sprite.LayerMapSet(CornerLayers.SE, sprite.AddLayerState(state0));
sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
@@ -64,14 +91,6 @@ private void OnStartup(EntityUid uid, IconSmoothComponent component, ComponentSt
sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
sprite.LayerMapSet(CornerLayers.SW, sprite.AddLayerState(state0));
sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
-
- if (component.Shader != null)
- {
- sprite.LayerSetShader(CornerLayers.SE, component.Shader);
- sprite.LayerSetShader(CornerLayers.NE, component.Shader);
- sprite.LayerSetShader(CornerLayers.NW, component.Shader);
- sprite.LayerSetShader(CornerLayers.SW, component.Shader);
- }
}
private void OnShutdown(EntityUid uid, IconSmoothComponent component, ComponentShutdown args)
diff --git a/Content.Client/Instruments/UI/BandMenu.xaml.cs b/Content.Client/Instruments/UI/BandMenu.xaml.cs
index 5fb293a194d..26cd1369e55 100644
--- a/Content.Client/Instruments/UI/BandMenu.xaml.cs
+++ b/Content.Client/Instruments/UI/BandMenu.xaml.cs
@@ -11,7 +11,9 @@ public sealed partial class BandMenu : DefaultWindow
{
private readonly InstrumentBoundUserInterface _owner;
- public BandMenu(InstrumentBoundUserInterface owner) : base()
+ public EntityUid? Master;
+
+ public BandMenu(InstrumentBoundUserInterface owner)
{
RobustXamlLoader.Load(this);
@@ -40,7 +42,7 @@ public void Populate((NetEntity, string)[] nearby, IEntityManager entManager)
{
var uid = entManager.GetEntity(nent);
var item = BandList.AddItem(name, null, true, uid);
- item.Selected = _owner.Instrument?.Master == uid;
+ item.Selected = Master == uid;
}
}
}
diff --git a/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs b/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs
index 2814d415365..c175e67842f 100644
--- a/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs
+++ b/Content.Client/Instruments/UI/ChannelsMenu.xaml.cs
@@ -51,7 +51,7 @@ private void OnClearPressed(BaseButton.ButtonEventArgs obj)
}
}
- public void Populate()
+ public void Populate(InstrumentComponent? instrument)
{
ChannelList.Clear();
@@ -60,7 +60,8 @@ public void Populate()
var item = ChannelList.AddItem(_owner.Loc.GetString("instrument-component-channel-name",
("number", i)), null, true, i);
- item.Selected = !_owner.Instrument?.FilteredChannels[i] ?? false;
+
+ item.Selected = !instrument?.FilteredChannels[i] ?? false;
}
}
}
diff --git a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs
index 0f5729f55b1..4816ce8c365 100644
--- a/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs
+++ b/Content.Client/Instruments/UI/InstrumentBoundUserInterface.cs
@@ -24,8 +24,6 @@ public sealed class InstrumentBoundUserInterface : BoundUserInterface
[ViewVariables] private BandMenu? _bandMenu;
[ViewVariables] private ChannelsMenu? _channelsMenu;
- [ViewVariables] public InstrumentComponent? Instrument { get; private set; }
-
public InstrumentBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
@@ -43,14 +41,20 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
protected override void Open()
{
- if (!EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument))
- return;
+ _instrumentMenu = this.CreateWindow();
+ _instrumentMenu.Title = EntMan.GetComponent(Owner).EntityName;
- Instrument = instrument;
- _instrumentMenu = new InstrumentMenu(this);
- _instrumentMenu.OnClose += Close;
+ _instrumentMenu.OnOpenBand += OpenBandMenu;
+ _instrumentMenu.OnOpenChannels += OpenChannelsMenu;
+ _instrumentMenu.OnCloseChannels += CloseChannelsMenu;
+ _instrumentMenu.OnCloseBands += CloseBandMenu;
- _instrumentMenu.OpenCentered();
+ _instrumentMenu.SetMIDI(MidiManager.IsAvailable);
+
+ if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument))
+ {
+ _instrumentMenu.SetInstrument((Owner, instrument));
+ }
}
protected override void Dispose(bool disposing)
@@ -58,7 +62,12 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
if (!disposing)
return;
- _instrumentMenu?.Dispose();
+
+ if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument))
+ {
+ _instrumentMenu?.RemoveInstrument(instrument);
+ }
+
_bandMenu?.Dispose();
_channelsMenu?.Dispose();
}
@@ -72,6 +81,11 @@ public void OpenBandMenu()
{
_bandMenu ??= new BandMenu(this);
+ if (EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument))
+ {
+ _bandMenu.Master = instrument.Master;
+ }
+
// Refresh cache...
RefreshBands();
@@ -87,7 +101,9 @@ public void CloseBandMenu()
public void OpenChannelsMenu()
{
_channelsMenu ??= new ChannelsMenu(this);
- _channelsMenu.Populate();
+ EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument);
+
+ _channelsMenu.Populate(instrument);
_channelsMenu.OpenCenteredRight();
}
diff --git a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
index da443e3fb5b..9b14e01fb57 100644
--- a/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
+++ b/Content.Client/Instruments/UI/InstrumentMenu.xaml.cs
@@ -1,7 +1,10 @@
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
+using Content.Client.Interactable;
+using Content.Shared.ActionBlocker;
using Robust.Client.AutoGenerated;
+using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -16,33 +19,23 @@ namespace Content.Client.Instruments.UI
[GenerateTypedNameReferences]
public sealed partial class InstrumentMenu : DefaultWindow
{
- private readonly InstrumentBoundUserInterface _owner;
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IFileDialogManager _dialogs = default!;
+ [Dependency] private readonly IPlayerManager _player = default!;
private bool _isMidiFileDialogueWindowOpen;
- public InstrumentMenu(InstrumentBoundUserInterface owner)
- {
- RobustXamlLoader.Load(this);
-
- _owner = owner;
+ public event Action? OnOpenBand;
+ public event Action? OnOpenChannels;
+ public event Action? OnCloseBands;
+ public event Action? OnCloseChannels;
- if (_owner.Instrument != null)
- {
- _owner.Instrument.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
- Title = _owner.Entities.GetComponent(_owner.Owner).EntityName;
- LoopButton.Disabled = !_owner.Instrument.IsMidiOpen;
- LoopButton.Pressed = _owner.Instrument.LoopMidi;
- ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
- StopButton.Disabled = !_owner.Instrument.IsMidiOpen;
- PlaybackSlider.MouseFilter = _owner.Instrument.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
- }
+ public EntityUid Entity;
- if (!_owner.MidiManager.IsAvailable)
- {
- UnavailableOverlay.Visible = true;
- // We return early as to not give the buttons behavior.
- return;
- }
+ public InstrumentMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
InputButton.OnToggled += MidiInputButtonOnOnToggled;
BandButton.OnPressed += BandButtonOnPressed;
@@ -57,12 +50,34 @@ public InstrumentMenu(InstrumentBoundUserInterface owner)
MinSize = SetSize = new Vector2(400, 150);
}
+ public void SetInstrument(Entity entity)
+ {
+ Entity = entity;
+ var component = entity.Comp;
+ component.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
+ LoopButton.Disabled = !component.IsMidiOpen;
+ LoopButton.Pressed = component.LoopMidi;
+ ChannelsButton.Disabled = !component.IsRendererAlive;
+ StopButton.Disabled = !component.IsMidiOpen;
+ PlaybackSlider.MouseFilter = component.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
+ }
+
+ public void RemoveInstrument(InstrumentComponent component)
+ {
+ component.OnMidiPlaybackEnded -= InstrumentOnMidiPlaybackEnded;
+ }
+
+ public void SetMIDI(bool available)
+ {
+ UnavailableOverlay.Visible = !available;
+ }
+
private void BandButtonOnPressed(ButtonEventArgs obj)
{
if (!PlayCheck())
return;
- _owner.OpenBandMenu();
+ OnOpenBand?.Invoke();
}
private void BandButtonOnToggled(ButtonToggledEventArgs obj)
@@ -70,12 +85,15 @@ private void BandButtonOnToggled(ButtonToggledEventArgs obj)
if (obj.Pressed)
return;
- _owner.Instruments.SetMaster(_owner.Owner, null);
+ if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
+ {
+ _entManager.System().SetMaster(Entity, instrument.Master);
+ }
}
private void ChannelsButtonOnPressed(ButtonEventArgs obj)
{
- _owner.OpenChannelsMenu();
+ OnOpenChannels?.Invoke();
}
private void InstrumentOnMidiPlaybackEnded()
@@ -85,8 +103,10 @@ private void InstrumentOnMidiPlaybackEnded()
public void MidiPlaybackSetButtonsDisabled(bool disabled)
{
- if(disabled)
- _owner.CloseChannelsMenu();
+ if (disabled)
+ {
+ OnCloseChannels?.Invoke();
+ }
LoopButton.Disabled = disabled;
StopButton.Disabled = disabled;
@@ -100,7 +120,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
if (_isMidiFileDialogueWindowOpen)
return;
- _owner.CloseBandMenu();
+ OnCloseBands?.Invoke();
var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
@@ -108,7 +128,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
// or focus the previously-opened window.
_isMidiFileDialogueWindowOpen = true;
- await using var file = await _owner.FileDialogManager.OpenFile(filters);
+ await using var file = await _dialogs.OpenFile(filters);
_isMidiFileDialogueWindowOpen = false;
@@ -129,9 +149,18 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
await file.CopyToAsync(memStream);
- if (_owner.Instrument is not {} instrument
- || !_owner.Instruments.OpenMidi(_owner.Owner, memStream.GetBuffer().AsSpan(0, (int) memStream.Length), instrument))
+ if (!_entManager.TryGetComponent(Entity, out var instrument))
+ {
return;
+ }
+
+ if (!_entManager.System()
+ .OpenMidi(Entity,
+ memStream.GetBuffer().AsSpan(0, (int) memStream.Length),
+ instrument))
+ {
+ return;
+ }
MidiPlaybackSetButtonsDisabled(false);
if (InputButton.Pressed)
@@ -140,7 +169,7 @@ private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
{
- _owner.CloseBandMenu();
+ OnCloseBands?.Invoke();
if (obj.Pressed)
{
@@ -148,109 +177,99 @@ private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
return;
MidiStopButtonOnPressed(null);
- if(_owner.Instrument is {} instrument)
- _owner.Instruments.OpenInput(_owner.Owner, instrument);
+
+ if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
+ _entManager.System().OpenInput(Entity, instrument);
}
- else if (_owner.Instrument is { } instrument)
+ else
{
- _owner.Instruments.CloseInput(_owner.Owner, false, instrument);
- _owner.CloseChannelsMenu();
+ _entManager.System().CloseInput(Entity, false);
+ OnCloseChannels?.Invoke();
}
}
private bool PlayCheck()
{
// TODO all of these checks should also be done server-side.
-
- var instrumentEnt = _owner.Owner;
- var instrument = _owner.Instrument;
-
- if (instrument == null)
+ if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
return false;
- var localEntity = _owner.PlayerManager.LocalEntity;
+ var localEntity = _player.LocalEntity;
// If we don't have a player or controlled entity, we return.
if (localEntity == null)
return false;
// By default, allow an instrument to play itself and skip all other checks
- if (localEntity == instrumentEnt)
+ if (localEntity == Entity)
return true;
- var container = _owner.Entities.System();
+ var container = _entManager.System();
// If we're a handheld instrument, we might be in a container. Get it just in case.
- container.TryGetContainingContainer(instrumentEnt, out var conMan);
+ container.TryGetContainingContainer((Entity, null, null), out var conMan);
// If the instrument is handheld and we're not holding it, we return.
- if ((instrument.Handheld && (conMan == null || conMan.Owner != localEntity)))
+ if (instrument.Handheld && (conMan == null || conMan.Owner != localEntity))
return false;
- if (!_owner.ActionBlocker.CanInteract(localEntity.Value, instrumentEnt))
+ if (!_entManager.System().CanInteract(localEntity.Value, Entity))
return false;
// We check that we're in range unobstructed just in case.
- return _owner.Interactions.InRangeUnobstructed(localEntity.Value, instrumentEnt);
+ return _entManager.System().InRangeUnobstructed(localEntity.Value, Entity);
}
private void MidiStopButtonOnPressed(ButtonEventArgs? obj)
{
MidiPlaybackSetButtonsDisabled(true);
- if (_owner.Instrument is not {} instrument)
- return;
-
- _owner.Instruments.CloseMidi(_owner.Owner, false, instrument);
- _owner.CloseChannelsMenu();
+ _entManager.System().CloseMidi(Entity, false);
+ OnCloseChannels?.Invoke();
}
private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj)
{
- if (_owner.Instrument == null)
- return;
+ var instrument = _entManager.System();
+
+ if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrumentComp))
+ {
+ instrumentComp.LoopMidi = obj.Pressed;
+ }
- _owner.Instrument.LoopMidi = obj.Pressed;
- _owner.Instruments.UpdateRenderer(_owner.Owner, _owner.Instrument);
+ instrument.UpdateRenderer(Entity);
}
private void PlaybackSliderSeek(Range _)
{
// Do not seek while still grabbing.
- if (PlaybackSlider.Grabbed || _owner.Instrument is not {} instrument)
+ if (PlaybackSlider.Grabbed)
return;
- _owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
+ _entManager.System().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
}
private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args)
{
- if (args.Function != EngineKeyFunctions.UIClick || _owner.Instrument is not {} instrument)
+ if (args.Function != EngineKeyFunctions.UIClick)
return;
- _owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
- }
-
- public override void Close()
- {
- base.Close();
- _owner.CloseBandMenu();
- _owner.CloseChannelsMenu();
+ _entManager.System().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- if (_owner.Instrument == null)
+ if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
return;
- var hasMaster = _owner.Instrument.Master != null;
+ var hasMaster = instrument.Master != null;
BandButton.ToggleMode = hasMaster;
BandButton.Pressed = hasMaster;
- BandButton.Disabled = _owner.Instrument.IsMidiOpen || _owner.Instrument.IsInputOpen;
- ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
+ BandButton.Disabled = instrument.IsMidiOpen || instrument.IsInputOpen;
+ ChannelsButton.Disabled = !instrument.IsRendererAlive;
- if (!_owner.Instrument.IsMidiOpen)
+ if (!instrument.IsMidiOpen)
{
PlaybackSlider.MaxValue = 1;
PlaybackSlider.SetValueWithoutEvent(0);
@@ -260,8 +279,8 @@ protected override void FrameUpdate(FrameEventArgs args)
if (PlaybackSlider.Grabbed)
return;
- PlaybackSlider.MaxValue = _owner.Instrument.PlayerTotalTick;
- PlaybackSlider.SetValueWithoutEvent(_owner.Instrument.PlayerTick);
+ PlaybackSlider.MaxValue = instrument.PlayerTotalTick;
+ PlaybackSlider.SetValueWithoutEvent(instrument.PlayerTick);
}
}
}
diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs
index d249766bbcc..41459995796 100644
--- a/Content.Client/Interaction/DragDropSystem.cs
+++ b/Content.Client/Interaction/DragDropSystem.cs
@@ -42,6 +42,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
// how often to recheck possible targets (prevents calling expensive
// check logic each update)
@@ -89,7 +90,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
///
private bool _isReplaying;
- private float _deadzone;
+ public float Deadzone;
private DragState _state = DragState.NotDragging;
@@ -121,7 +122,7 @@ public override void Initialize()
private void SetDeadZone(float deadZone)
{
- _deadzone = deadZone;
+ Deadzone = deadZone;
}
public override void Shutdown()
@@ -211,7 +212,7 @@ private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args)
_draggedEntity = entity;
_state = DragState.MouseDown;
- _mouseDownScreenPos = _inputManager.MouseScreenPosition;
+ _mouseDownScreenPos = args.ScreenCoordinates;
_mouseDownTime = 0;
// don't want anything else to process the click,
@@ -239,8 +240,13 @@ private void StartDrag()
if (TryComp(_draggedEntity, out var draggedSprite))
{
+ var screenPos = _inputManager.MouseScreenPosition;
+ // No _draggedEntity in null window (Happens in tests)
+ if (!screenPos.IsValid)
+ return;
+
// pop up drag shadow under mouse
- var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
+ var mousePos = _eyeManager.PixelToMap(screenPos);
_dragShadow = EntityManager.SpawnEntity("dragshadow", mousePos);
var dragSprite = Comp(_dragShadow.Value);
dragSprite.CopyFrom(draggedSprite);
@@ -517,6 +523,9 @@ private void RemoveHighlights()
if (dropEv2.Handled)
return dropEv2.CanDrop;
+ if (dropEv.Handled && dropEv.CanDrop)
+ return true;
+
return null;
}
@@ -530,7 +539,7 @@ public override void Update(float frameTime)
case DragState.MouseDown:
{
var screenPos = _inputManager.MouseScreenPosition;
- if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length() > _deadzone)
+ if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length() > Deadzone)
{
StartDrag();
}
@@ -551,7 +560,7 @@ public override void FrameUpdate(float frameTime)
if (Exists(_dragShadow))
{
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
- Transform(_dragShadow.Value).WorldPosition = mousePos.Position;
+ _transformSystem.SetWorldPosition(_dragShadow.Value, mousePos.Position);
}
}
}
diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs
index 7e50eb1c68a..132c5ed654c 100644
--- a/Content.Client/Inventory/StrippableBoundUserInterface.cs
+++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs
@@ -41,7 +41,7 @@ public sealed class StrippableBoundUserInterface : BoundUserInterface
public const string HiddenPocketEntityId = "StrippingHiddenEntity";
[ViewVariables]
- private readonly StrippingMenu? _strippingMenu;
+ private StrippingMenu? _strippingMenu;
[ViewVariables]
private readonly EntityUid _virtualHiddenEntity;
@@ -51,33 +51,30 @@ public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u
_examine = EntMan.System();
_inv = EntMan.System();
_cuffable = EntMan.System();
-
- // TODO update name when identity changes
- var title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan)));
- _strippingMenu = new StrippingMenu(title, this);
- _strippingMenu.OnClose += Close;
-
- // TODO use global entity
- // BUIs are opened and closed while applying comp sates, so spawning entities here is probably not the best idea.
_virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
}
protected override void Open()
{
base.Open();
+
+ _strippingMenu = this.CreateWindow();
+ _strippingMenu.OnDirty += UpdateMenu;
+ _strippingMenu.Title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan)));
+
_strippingMenu?.OpenCenteredLeft();
}
protected override void Dispose(bool disposing)
{
- base.Dispose(disposing);
-
- EntMan.DeleteEntity(_virtualHiddenEntity);
-
if (!disposing)
return;
- _strippingMenu?.Dispose();
+ if (_strippingMenu != null)
+ _strippingMenu.OnDirty -= UpdateMenu;
+
+ EntMan.DeleteEntity(_virtualHiddenEntity);
+ base.Dispose(disposing);
}
public void DirtyMenu()
diff --git a/Content.Client/Items/Systems/ItemSystem.cs b/Content.Client/Items/Systems/ItemSystem.cs
index 5e60d06d0ce..2b5a41c6ce7 100644
--- a/Content.Client/Items/Systems/ItemSystem.cs
+++ b/Content.Client/Items/Systems/ItemSystem.cs
@@ -43,7 +43,7 @@ private void OnEquipped(EntityUid uid, SpriteComponent component, GotEquippedEve
public override void VisualsChanged(EntityUid uid)
{
// if the item is in a container, it might be equipped to hands or inventory slots --> update visuals.
- if (Container.TryGetContainingContainer(uid, out var container))
+ if (Container.TryGetContainingContainer((uid, null, null), out var container))
RaiseLocalEvent(container.Owner, new VisualsChangedEvent(GetNetEntity(uid), container.ID));
}
diff --git a/Content.Client/Items/Systems/ItemToggleSystem.cs b/Content.Client/Items/Systems/ItemToggleSystem.cs
deleted file mode 100644
index 46d6f1b464d..00000000000
--- a/Content.Client/Items/Systems/ItemToggleSystem.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Content.Shared.Item.ItemToggle;
-
-namespace Content.Shared.Item;
-
-///
-public sealed class ItemToggleSystem : SharedItemToggleSystem
-{
-
-}
diff --git a/Content.Client/Jittering/JitteringSystem.cs b/Content.Client/Jittering/JitteringSystem.cs
index 185bd490d3b..0c11a139635 100644
--- a/Content.Client/Jittering/JitteringSystem.cs
+++ b/Content.Client/Jittering/JitteringSystem.cs
@@ -48,6 +48,9 @@ private void OnAnimationCompleted(EntityUid uid, JitteringComponent jittering, A
if(args.Key != _jitterAnimationKey)
return;
+ if (!args.Finished)
+ return;
+
if (TryComp(uid, out AnimationPlayerComponent? animationPlayer)
&& TryComp(uid, out SpriteComponent? sprite))
_animationPlayer.Play(uid, animationPlayer, GetAnimation(jittering, sprite), _jitterAnimationKey);
diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
index f97d8a73302..7884268c428 100644
--- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
+++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
@@ -12,42 +12,34 @@ namespace Content.Client.Kitchen.UI
[GenerateTypedNameReferences]
public sealed partial class GrinderMenu : FancyWindow
{
- private readonly IEntityManager _entityManager;
- private readonly IPrototypeManager _prototypeManager;
- private readonly ReagentGrinderBoundUserInterface _owner;
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly Dictionary _chamberVisualContents = new();
- public GrinderMenu(ReagentGrinderBoundUserInterface owner, IEntityManager entityManager, IPrototypeManager prototypeManager)
+ public event Action? OnToggleAuto;
+ public event Action? OnGrind;
+ public event Action? OnJuice;
+ public event Action? OnEjectAll;
+ public event Action? OnEjectBeaker;
+ public event Action? OnEjectChamber;
+
+ public GrinderMenu()
{
RobustXamlLoader.Load(this);
- _entityManager = entityManager;
- _prototypeManager = prototypeManager;
- _owner = owner;
- AutoModeButton.OnPressed += owner.ToggleAutoMode;
- GrindButton.OnPressed += owner.StartGrinding;
- JuiceButton.OnPressed += owner.StartJuicing;
- ChamberContentBox.EjectButton.OnPressed += owner.EjectAll;
- BeakerContentBox.EjectButton.OnPressed += owner.EjectBeaker;
+ IoCManager.InjectDependencies(this);
+ AutoModeButton.OnPressed += _ => OnToggleAuto?.Invoke();
+ GrindButton.OnPressed += _ => OnGrind?.Invoke();
+ JuiceButton.OnPressed += _ => OnJuice?.Invoke();
+ ChamberContentBox.EjectButton.OnPressed += _ => OnEjectAll?.Invoke();
+ BeakerContentBox.EjectButton.OnPressed += _ => OnEjectBeaker?.Invoke();
ChamberContentBox.BoxContents.OnItemSelected += OnChamberBoxContentsItemSelected;
BeakerContentBox.BoxContents.SelectMode = ItemList.ItemListSelectMode.None;
}
private void OnChamberBoxContentsItemSelected(ItemList.ItemListSelectedEventArgs args)
{
- _owner.EjectChamberContent(_chamberVisualContents[args.ItemIndex]);
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- _chamberVisualContents.Clear();
- GrindButton.OnPressed -= _owner.StartGrinding;
- JuiceButton.OnPressed -= _owner.StartJuicing;
- ChamberContentBox.EjectButton.OnPressed -= _owner.EjectAll;
- BeakerContentBox.EjectButton.OnPressed -= _owner.EjectBeaker;
- ChamberContentBox.BoxContents.OnItemSelected -= OnChamberBoxContentsItemSelected;
+ OnEjectChamber?.Invoke(_chamberVisualContents[args.ItemIndex]);
}
public void UpdateState(ReagentGrinderInterfaceState state)
diff --git a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs
index 7e7dd2d6935..643ac47054b 100644
--- a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs
+++ b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs
@@ -3,6 +3,7 @@
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing;
@@ -19,28 +20,15 @@ public sealed class MicrowaveBoundUserInterface : BoundUserInterface
[ViewVariables]
private readonly Dictionary _reagents = new();
- [Dependency] private readonly IGameTiming _gameTiming = default!;
-
- public MicrowaveUpdateUserInterfaceState currentState = default!;
-
- private IEntityManager _entManager;
public MicrowaveBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
- _entManager = IoCManager.Resolve();
- }
-
- public TimeSpan GetCurrentTime()
- {
- return _gameTiming.CurTime;
}
protected override void Open()
{
base.Open();
- _menu = new MicrowaveMenu(this);
- _menu.OpenCentered();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.StartButton.OnPressed += _ => SendPredictedMessage(new MicrowaveStartCookMessage());
_menu.EjectButton.OnPressed += _ => SendPredictedMessage(new MicrowaveEjectMessage());
_menu.IngredientsList.OnItemSelected += args =>
@@ -74,38 +62,23 @@ protected override void Open()
};
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (!disposing)
- {
- return;
- }
-
- _solids.Clear();
- _menu?.Dispose();
- }
-
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
- if (state is not MicrowaveUpdateUserInterfaceState cState)
+ if (state is not MicrowaveUpdateUserInterfaceState cState || _menu == null)
{
return;
}
+ _menu.IsBusy = cState.IsMicrowaveBusy;
+ _menu.CurrentCooktimeEnd = cState.CurrentCookTimeEnd;
- _menu?.ToggleBusyDisableOverlayPanel(cState.IsMicrowaveBusy || cState.ContainedSolids.Length == 0);
- currentState = cState;
-
+ _menu.ToggleBusyDisableOverlayPanel(cState.IsMicrowaveBusy || cState.ContainedSolids.Length == 0);
// TODO move this to a component state and ensure the net ids.
- RefreshContentsDisplay(_entManager.GetEntityArray(cState.ContainedSolids));
-
- if (_menu == null) return;
+ RefreshContentsDisplay(EntMan.GetEntityArray(cState.ContainedSolids));
//Set the cook time info label
- var cookTime = cState.ActiveButtonIndex == 0
+ var cookTime = cState.ActiveButtonIndex == 0
? Loc.GetString("microwave-menu-instant-button")
: cState.CurrentCookTime.ToString();
diff --git a/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs b/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs
index b292e9f1465..13029e38469 100644
--- a/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs
+++ b/Content.Client/Kitchen/UI/MicrowaveMenu.xaml.cs
@@ -9,22 +9,21 @@ namespace Content.Client.Kitchen.UI
[GenerateTypedNameReferences]
public sealed partial class MicrowaveMenu : FancyWindow
{
- public sealed class MicrowaveCookTimeButton : Button
- {
- public uint CookTime;
- }
+ [Dependency] private readonly IGameTiming _timing = default!;
public event Action? OnCookTimeSelected;
public ButtonGroup CookTimeButtonGroup { get; }
- private readonly MicrowaveBoundUserInterface _owner;
- public MicrowaveMenu(MicrowaveBoundUserInterface owner)
+ public bool IsBusy;
+ public TimeSpan CurrentCooktimeEnd;
+
+ public MicrowaveMenu()
{
RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
CookTimeButtonGroup = new ButtonGroup();
InstantCookButton.Group = CookTimeButtonGroup;
- _owner = owner;
InstantCookButton.OnPressed += args =>
{
OnCookTimeSelected?.Invoke(args, 0);
@@ -65,14 +64,20 @@ public void ToggleBusyDisableOverlayPanel(bool shouldDisable)
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- if(!_owner.currentState.IsMicrowaveBusy)
+
+ if (!IsBusy)
return;
- if(_owner.currentState.CurrentCookTimeEnd > _owner.GetCurrentTime())
+ if (CurrentCooktimeEnd > _timing.CurTime)
{
CookTimeInfoLabel.Text = Loc.GetString("microwave-bound-user-interface-cook-time-label",
- ("time",_owner.currentState.CurrentCookTimeEnd.Subtract(_owner.GetCurrentTime()).Seconds));
+ ("time", CurrentCooktimeEnd.Subtract(_timing.CurTime).Seconds));
}
}
+
+ public sealed class MicrowaveCookTimeButton : Button
+ {
+ public uint CookTime;
+ }
}
}
diff --git a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
index e6f108b3050..bc4cc75b4d1 100644
--- a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
+++ b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Kitchen;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Prototypes;
@@ -8,8 +9,6 @@ namespace Content.Client.Kitchen.UI
{
public sealed class ReagentGrinderBoundUserInterface : BoundUserInterface
{
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-
[ViewVariables]
private GrinderMenu? _menu;
@@ -21,20 +20,13 @@ protected override void Open()
{
base.Open();
- _menu = new GrinderMenu(this, EntMan, _prototypeManager);
- _menu.OpenCentered();
- _menu.OnClose += Close;
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- {
- return;
- }
-
- _menu?.Dispose();
+ _menu = this.CreateWindow();
+ _menu.OnToggleAuto += ToggleAutoMode;
+ _menu.OnGrind += StartGrinding;
+ _menu.OnJuice += StartJuicing;
+ _menu.OnEjectAll += EjectAll;
+ _menu.OnEjectBeaker += EjectBeaker;
+ _menu.OnEjectChamber += EjectChamberContent;
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -52,27 +44,27 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
_menu?.HandleMessage(message);
}
- public void ToggleAutoMode(BaseButton.ButtonEventArgs args)
+ public void ToggleAutoMode()
{
SendMessage(new ReagentGrinderToggleAutoModeMessage());
}
- public void StartGrinding(BaseButton.ButtonEventArgs? _ = null)
+ public void StartGrinding()
{
SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind));
}
- public void StartJuicing(BaseButton.ButtonEventArgs? _ = null)
+ public void StartJuicing()
{
SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Juice));
}
- public void EjectAll(BaseButton.ButtonEventArgs? _ = null)
+ public void EjectAll()
{
SendMessage(new ReagentGrinderEjectChamberAllMessage());
}
- public void EjectBeaker(BaseButton.ButtonEventArgs? _ = null)
+ public void EjectBeaker()
{
SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinder.BeakerSlotId));
}
diff --git a/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs b/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs
index 555f1ff09e6..6b656123412 100644
--- a/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs
+++ b/Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Labels;
using Content.Shared.Labels.Components;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Labels.UI
{
@@ -23,13 +24,8 @@ protected override void Open()
{
base.Open();
- _window = new HandLabelerWindow();
- if (State != null)
- UpdateState(State);
+ _window = this.CreateWindow();
- _window.OpenCentered();
-
- _window.OnClose += Close;
_window.OnLabelChanged += OnLabelChanged;
Reload();
}
@@ -51,13 +47,5 @@ public void Reload()
_window.SetCurrentLabel(component.AssignedLabel);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
-
}
diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs
index 62a06629f29..13cf281513d 100644
--- a/Content.Client/LateJoin/LateJoinGui.cs
+++ b/Content.Client/LateJoin/LateJoinGui.cs
@@ -2,9 +2,11 @@
using System.Numerics;
using Content.Client.CrewManifest;
using Content.Client.GameTicking.Managers;
+using Content.Client.Lobby;
using Content.Client.UserInterface.Controls;
using Content.Client.Players.PlayTimeTracking;
using Content.Shared.CCVar;
+using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.StatusIcon;
using Robust.Client.Console;
@@ -26,6 +28,7 @@ public sealed class LateJoinGui : DefaultWindow
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
+ [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
public event Action<(NetEntity, string)> SelectedId;
@@ -254,7 +257,7 @@ private void RebuildUI()
jobButton.OnPressed += _ => SelectedId.Invoke((id, jobButton.JobId));
- if (!_jobRequirements.IsAllowed(prototype, out var reason))
+ if (!_jobRequirements.IsAllowed(prototype, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
jobButton.Disabled = true;
diff --git a/Content.Client/Lathe/LatheSystem.cs b/Content.Client/Lathe/LatheSystem.cs
index c72f01b39b1..386520c198f 100644
--- a/Content.Client/Lathe/LatheSystem.cs
+++ b/Content.Client/Lathe/LatheSystem.cs
@@ -22,21 +22,29 @@ private void OnAppearanceChange(EntityUid uid, LatheComponent component, ref App
if (args.Sprite == null)
return;
+ // Lathe specific stuff
+ if (_appearance.TryGetData(uid, LatheVisuals.IsRunning, out var isRunning, args.Component))
+ {
+ if (args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
+ component.RunningState != null &&
+ component.IdleState != null)
+ {
+ var state = isRunning ? component.RunningState : component.IdleState;
+ args.Sprite.LayerSetState(runningLayer, state);
+ }
+ }
+
if (_appearance.TryGetData(uid, PowerDeviceVisuals.Powered, out var powered, args.Component) &&
args.Sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out var powerLayer))
{
args.Sprite.LayerSetVisible(powerLayer, powered);
- }
- // Lathe specific stuff
- if (_appearance.TryGetData(uid, LatheVisuals.IsRunning, out var isRunning, args.Component) &&
- args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
- component.RunningState != null &&
- component.IdleState != null)
- {
- var state = isRunning ? component.RunningState : component.IdleState;
- args.Sprite.LayerSetAnimationTime(runningLayer, 0f);
- args.Sprite.LayerSetState(runningLayer, state);
+ if (component.UnlitIdleState != null &&
+ component.UnlitRunningState != null)
+ {
+ var state = isRunning ? component.UnlitRunningState : component.UnlitIdleState;
+ args.Sprite.LayerSetState(powerLayer, state);
+ }
}
}
diff --git a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs
index 6e6d1b91761..a599f79152e 100644
--- a/Content.Client/Lathe/UI/LatheBoundUserInterface.cs
+++ b/Content.Client/Lathe/UI/LatheBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Lathe;
using Content.Shared.Research.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Lathe.UI
{
@@ -17,9 +18,9 @@ protected override void Open()
{
base.Open();
- _menu = new LatheMenu(this);
- _menu.OnClose += Close;
-
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
+ _menu.OpenCenteredRight();
_menu.OnServerListButtonPressed += _ =>
{
@@ -30,8 +31,6 @@ protected override void Open()
{
SendMessage(new LatheQueueRecipeMessage(recipe, amount));
};
-
- _menu.OpenCenteredRight();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -50,13 +49,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
break;
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Dispose();
- }
}
}
diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml
index 6f484d8c7be..5b21f0bae66 100644
--- a/Content.Client/Lathe/UI/LatheMenu.xaml
+++ b/Content.Client/Lathe/UI/LatheMenu.xaml
@@ -100,12 +100,9 @@
Margin="5 0 0 0"
Text="{Loc 'lathe-menu-fabricating-message'}">
-
-
+
-
-
+
+
+
+
? CurrentCategory;
- public LatheMenu(LatheBoundUserInterface owner)
+ public EntityUid Entity;
+
+ public LatheMenu()
{
- _owner = owner.Owner;
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -43,8 +44,6 @@ public LatheMenu(LatheBoundUserInterface owner)
_lathe = _entityManager.System();
_materialStorage = _entityManager.System();
- Title = _entityManager.GetComponent(owner.Owner).EntityName;
-
SearchBar.OnTextChanged += _ =>
{
PopulateRecipes();
@@ -57,8 +56,13 @@ public LatheMenu(LatheBoundUserInterface owner)
FilterOption.OnItemSelected += OnItemSelected;
ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a);
+ }
+
+ public void SetEntity(EntityUid uid)
+ {
+ Entity = uid;
- if (_entityManager.TryGetComponent(owner.Owner, out var latheComponent))
+ if (_entityManager.TryGetComponent(Entity, out var latheComponent))
{
if (!latheComponent.DynamicRecipes.Any())
{
@@ -66,7 +70,7 @@ public LatheMenu(LatheBoundUserInterface owner)
}
}
- MaterialsList.SetOwner(owner.Owner);
+ MaterialsList.SetOwner(Entity);
}
///
@@ -85,7 +89,7 @@ public void PopulateRecipes()
if (SearchBar.Text.Trim().Length != 0)
{
- if (proto.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
+ if (_lathe.GetRecipeName(recipe).ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
recipesToShow.Add(proto);
}
else
@@ -97,16 +101,15 @@ public void PopulateRecipes()
if (!int.TryParse(AmountLineEdit.Text, out var quantity) || quantity <= 0)
quantity = 1;
- var sortedRecipesToShow = recipesToShow.OrderBy(p => p.Name);
+ var sortedRecipesToShow = recipesToShow.OrderBy(_lathe.GetRecipeName);
RecipeList.Children.Clear();
+ _entityManager.TryGetComponent(Entity, out LatheComponent? lathe);
+
foreach (var prototype in sortedRecipesToShow)
{
- var icon = prototype.Icon == null
- ? _spriteSystem.GetPrototypeIcon(prototype.Result).Default
- : _spriteSystem.Frame0(prototype.Icon);
- var canProduce = _lathe.CanProduce(_owner, prototype, quantity);
+ var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe);
- var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, icon);
+ var control = new RecipeControl(_lathe, prototype, () => GenerateTooltipText(prototype), canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s =>
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
@@ -120,19 +123,20 @@ public void PopulateRecipes()
private string GenerateTooltipText(LatheRecipePrototype prototype)
{
StringBuilder sb = new();
+ var multiplier = _entityManager.GetComponent(Entity).MaterialUseMultiplier;
- foreach (var (id, amount) in prototype.RequiredMaterials)
+ foreach (var (id, amount) in prototype.Materials)
{
- if (!_prototypeManager.TryIndex(id, out var proto))
+ if (!_prototypeManager.TryIndex(id, out var proto))
continue;
- var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, _entityManager.GetComponent(_owner).MaterialUseMultiplier);
+ var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);
var sheetVolume = _materialStorage.GetSheetVolume(proto);
var unit = Loc.GetString(proto.Unit);
var sheets = adjustedAmount / (float) sheetVolume;
- var availableAmount = _materialStorage.GetMaterialAmount(_owner, id);
+ var availableAmount = _materialStorage.GetMaterialAmount(Entity, id);
var missingAmount = Math.Max(0, adjustedAmount - availableAmount);
var missingSheets = missingAmount / (float) sheetVolume;
@@ -152,8 +156,9 @@ private string GenerateTooltipText(LatheRecipePrototype prototype)
sb.AppendLine(tooltipText);
}
- if (!string.IsNullOrWhiteSpace(prototype.Description))
- sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description)));
+ var desc = _lathe.GetRecipeDescription(prototype);
+ if (!string.IsNullOrWhiteSpace(desc))
+ sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", desc)));
// Remove last newline
if (sb.Length > 0)
@@ -203,14 +208,20 @@ public void UpdateCategories()
///
public void PopulateQueueList(List queue)
{
- QueueList.Clear();
+ QueueList.DisposeAllChildren();
+
var idx = 1;
foreach (var recipe in queue)
{
- var icon = recipe.Icon == null
- ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default
- : _spriteSystem.Frame0(recipe.Icon);
- QueueList.AddItem($"{idx}. {recipe.Name}", icon);
+ var queuedRecipeBox = new BoxContainer();
+ queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
+
+ queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe));
+
+ var queuedRecipeLabel = new Label();
+ queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}";
+ queuedRecipeBox.AddChild(queuedRecipeLabel);
+ QueueList.AddChild(queuedRecipeBox);
idx++;
}
}
@@ -220,10 +231,30 @@ public void SetQueueInfo(LatheRecipePrototype? recipe)
FabricatingContainer.Visible = recipe != null;
if (recipe == null)
return;
- Icon.Texture = recipe.Icon == null
- ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default
- : _spriteSystem.Frame0(recipe.Icon);
- NameLabel.Text = $"{recipe.Name}";
+
+ FabricatingDisplayContainer.Children.Clear();
+ FabricatingDisplayContainer.AddChild(GetRecipeDisplayControl(recipe));
+
+ NameLabel.Text = _lathe.GetRecipeName(recipe);
+ }
+
+ public Control GetRecipeDisplayControl(LatheRecipePrototype recipe)
+ {
+ if (recipe.Icon != null)
+ {
+ var textRect = new TextureRect();
+ textRect.Texture = _spriteSystem.Frame0(recipe.Icon);
+ return textRect;
+ }
+
+ if (recipe.Result is { } result)
+ {
+ var entProtoView = new EntityPrototypeView();
+ entProtoView.SetPrototype(result);
+ return entProtoView;
+ }
+
+ return new Control();
}
private void OnItemSelected(OptionButton.ItemSelectedEventArgs obj)
diff --git a/Content.Client/Lathe/UI/RecipeControl.xaml b/Content.Client/Lathe/UI/RecipeControl.xaml
index 2e02c8a6147..1105ab478ff 100644
--- a/Content.Client/Lathe/UI/RecipeControl.xaml
+++ b/Content.Client/Lathe/UI/RecipeControl.xaml
@@ -5,11 +5,13 @@
Margin="0"
StyleClasses="ButtonSquare">
-
+ />
diff --git a/Content.Client/Lathe/UI/RecipeControl.xaml.cs b/Content.Client/Lathe/UI/RecipeControl.xaml.cs
index bf85ff7d938..4f438c8a8e5 100644
--- a/Content.Client/Lathe/UI/RecipeControl.xaml.cs
+++ b/Content.Client/Lathe/UI/RecipeControl.xaml.cs
@@ -1,9 +1,7 @@
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
-using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Graphics;
namespace Content.Client.Lathe.UI;
@@ -13,12 +11,12 @@ public sealed partial class RecipeControl : Control
public Action? OnButtonPressed;
public Func TooltipTextSupplier;
- public RecipeControl(LatheRecipePrototype recipe, Func tooltipTextSupplier, bool canProduce, Texture? texture = null)
+ public RecipeControl(LatheSystem latheSystem, LatheRecipePrototype recipe, Func tooltipTextSupplier, bool canProduce, Control displayControl)
{
RobustXamlLoader.Load(this);
- RecipeName.Text = recipe.Name;
- RecipeTexture.Texture = texture;
+ RecipeName.Text = latheSystem.GetRecipeName(recipe);
+ RecipeDisplayContainer.AddChild(displayControl);
Button.Disabled = !canProduce;
TooltipTextSupplier = tooltipTextSupplier;
Button.TooltipSupplier = SupplyTooltip;
diff --git a/Content.Client/Launcher/LauncherConnectingGui.xaml.cs b/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
index ac74ad7b60d..5015b710eb4 100644
--- a/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
+++ b/Content.Client/Launcher/LauncherConnectingGui.xaml.cs
@@ -116,7 +116,7 @@ private void HandleDisconnectReason(INetStructuredReason? reason)
private void ChangeLoginTip()
{
var tipsDataset = _cfg.GetCVar(CCVars.LoginTipsDataset);
- var loginTipsEnabled = _prototype.TryIndex(tipsDataset, out var tips);
+ var loginTipsEnabled = _prototype.TryIndex(tipsDataset, out var tips);
LoginTips.Visible = loginTipsEnabled;
if (!loginTipsEnabled)
@@ -131,7 +131,7 @@ private void ChangeLoginTip()
var randomIndex = _random.Next(tipList.Count);
var tip = tipList[randomIndex];
- LoginTip.SetMessage(tip);
+ LoginTip.SetMessage(Loc.GetString(tip));
LoginTipTitle.Text = Loc.GetString("connecting-window-tip", ("numberTip", randomIndex));
}
diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
index 11f69165cf6..ca19d8522c5 100644
--- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
+++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs
@@ -19,6 +19,9 @@ public override void Initialize()
private void OnBehaviorAnimationCompleted(EntityUid uid, LightBehaviourComponent component, AnimationCompletedEvent args)
{
+ if (!args.Finished)
+ return;
+
var container = component.Animations.FirstOrDefault(x => x.FullKey == args.Key);
if (container == null)
diff --git a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
index 842c13dedfe..5c2c4e4c875 100644
--- a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
+++ b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs
@@ -69,6 +69,9 @@ private void OnAfterAutoHandleState(EntityUid uid, RotatingLightComponent comp,
private void OnAnimationComplete(EntityUid uid, RotatingLightComponent comp, AnimationCompletedEvent args)
{
+ if (!args.Finished)
+ return;
+
PlayAnimation(uid, comp);
}
diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs
index e4a13ed8c6b..3cf98c98aba 100644
--- a/Content.Client/Lobby/LobbyUIController.cs
+++ b/Content.Client/Lobby/LobbyUIController.cs
@@ -43,9 +43,11 @@ public sealed class LobbyUIController : UIController, IOnStateEntered
/// This is the characher preview panel in the chat. This should only update if their character updates.
@@ -214,6 +216,46 @@ private void SaveProfile()
ReloadCharacterSetup();
}
+ private void CloseProfileEditor()
+ {
+ if (_profileEditor == null)
+ return;
+
+ _profileEditor.SetProfile(null, null);
+ _profileEditor.Visible = false;
+
+ if (_stateManager.CurrentState is LobbyState lobbyGui)
+ {
+ lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default);
+ }
+ }
+
+ private void OpenSavePanel()
+ {
+ if (_savePanel is { IsOpen: true })
+ return;
+
+ _savePanel = new CharacterSetupGuiSavePanel();
+
+ _savePanel.SaveButton.OnPressed += _ =>
+ {
+ SaveProfile();
+
+ _savePanel.Close();
+
+ CloseProfileEditor();
+ };
+
+ _savePanel.NoSaveButton.OnPressed += _ =>
+ {
+ _savePanel.Close();
+
+ CloseProfileEditor();
+ };
+
+ _savePanel.OpenCentered();
+ }
+
private (CharacterSetupGui, HumanoidProfileEditor) EnsureGui()
{
if (_characterSetup != null && _profileEditor != null)
@@ -231,6 +273,7 @@ private void SaveProfile()
_logManager,
_playerManager,
_prototypeManager,
+ _resourceCache,
_requirements,
_markings);
@@ -240,14 +283,16 @@ private void SaveProfile()
_characterSetup.CloseButton.OnPressed += _ =>
{
- // Reset sliders etc.
- _profileEditor.SetProfile(null, null);
- _profileEditor.Visible = false;
-
- if (_stateManager.CurrentState is LobbyState lobbyGui)
+ // Open the save panel if we have unsaved changes.
+ if (_profileEditor.Profile != null && _profileEditor.IsDirty)
{
- lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default);
+ OpenSavePanel();
+
+ return;
}
+
+ // Reset sliders etc.
+ CloseProfileEditor();
};
_profileEditor.Save += SaveProfile;
@@ -321,7 +366,7 @@ public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
- _spawn.EquipStartingGear(uid, _prototypeManager.Index(loadoutProto.Equipment));
+ _spawn.EquipStartingGear(uid, loadoutProto);
}
}
}
@@ -344,36 +389,51 @@ public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profil
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
- // TODO: Need some way to apply starting gear to an entity coz holy fucking shit dude.
- var loadoutGear = _prototypeManager.Index(loadoutProto.Equipment);
-
+ // TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
- var itemType = loadoutGear.GetGear(slot.Name);
-
- if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
+ // Try startinggear first
+ if (_prototypeManager.TryIndex(loadoutProto.StartingGear, out var loadoutGear))
{
- EntityManager.DeleteEntity(unequippedItem.Value);
+ var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
+
+ if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
+ {
+ EntityManager.DeleteEntity(unequippedItem.Value);
+ }
+
+ if (itemType != string.Empty)
+ {
+ var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
+ _inventory.TryEquip(dummy, item, slot.Name, true, true);
+ }
}
-
- if (itemType != string.Empty)
+ else
{
- var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
- _inventory.TryEquip(dummy, item, slot.Name, true, true);
+ var itemType = ((IEquipmentLoadout) loadoutProto).GetGear(slot.Name);
+
+ if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
+ {
+ EntityManager.DeleteEntity(unequippedItem.Value);
+ }
+
+ if (itemType != string.Empty)
+ {
+ var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
+ _inventory.TryEquip(dummy, item, slot.Name, true, true);
+ }
}
}
}
}
}
- if (job.StartingGear == null)
+ if (!_prototypeManager.TryIndex(job.StartingGear, out var gear))
return;
- var gear = _prototypeManager.Index(job.StartingGear);
-
foreach (var slot in slots)
{
- var itemType = gear.GetGear(slot.Name);
+ var itemType = ((IEquipmentLoadout) gear).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
diff --git a/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml b/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml
new file mode 100644
index 00000000000..2dcf9143533
--- /dev/null
+++ b/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml.cs b/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml.cs
new file mode 100644
index 00000000000..5f2690b7a89
--- /dev/null
+++ b/Content.Client/Lobby/UI/CharacterSetupGuiSavePanel.xaml.cs
@@ -0,0 +1,21 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Lobby.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class CharacterSetupGuiSavePanel : DefaultWindow
+{
+ public CharacterSetupGuiSavePanel()
+ {
+ RobustXamlLoader.Load(this);
+
+ CancelButton.OnPressed += _ =>
+ {
+ Close();
+ };
+
+ CloseButton.Visible = false;
+ }
+}
diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml
index 03a205e94a8..2f6c2d5aa23 100644
--- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml
+++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml
@@ -38,6 +38,8 @@
+
+
diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
index d41472bfe1a..558b68ab930 100644
--- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
+++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
@@ -6,6 +6,7 @@
using Content.Client.Lobby.UI.Roles;
using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking;
+using Content.Client.Sprite;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared.CCVar;
@@ -27,6 +28,7 @@
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Configuration;
+using Robust.Shared.ContentPack;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -43,6 +45,7 @@ public sealed partial class HumanoidProfileEditor : BoxContainer
private readonly IFileDialogManager _dialogManager;
private readonly IPlayerManager _playerManager;
private readonly IPrototypeManager _prototypeManager;
+ private readonly IResourceManager _resManager;
private readonly MarkingManager _markingManager;
private readonly JobRequirementsManager _requirements;
private readonly LobbyUIController _controller;
@@ -54,6 +57,7 @@ public sealed partial class HumanoidProfileEditor : BoxContainer
private LoadoutWindow? _loadoutWindow;
private bool _exporting;
+ private bool _imaging;
///
/// If we're attempting to save.
@@ -107,6 +111,7 @@ public HumanoidProfileEditor(
ILogManager logManager,
IPlayerManager playerManager,
IPrototypeManager prototypeManager,
+ IResourceManager resManager,
JobRequirementsManager requirements,
MarkingManager markings)
{
@@ -119,6 +124,7 @@ public HumanoidProfileEditor(
_prototypeManager = prototypeManager;
_markingManager = markings;
_preferencesManager = preferencesManager;
+ _resManager = resManager;
_requirements = requirements;
_controller = UserInterfaceManager.GetUIController();
@@ -132,6 +138,16 @@ public HumanoidProfileEditor(
ExportProfile();
};
+ ExportImageButton.OnPressed += args =>
+ {
+ ExportImage();
+ };
+
+ OpenImagesButton.OnPressed += args =>
+ {
+ _resManager.UserData.OpenOsWindow(ContentSpriteSystem.Exports);
+ };
+
ResetButton.OnPressed += args =>
{
SetProfile((HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter, _preferencesManager.Preferences?.SelectedCharacterIndex);
@@ -424,7 +440,6 @@ public HumanoidProfileEditor(
SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
UpdateSpeciesGuidebookIcon();
- ReloadPreview();
IsDirty = false;
}
@@ -634,7 +649,7 @@ public void RefreshAntags()
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
var requirements = _entManager.System().GetAntagRequirement(antag);
- if (!_requirements.CheckRoleTime(requirements, out var reason))
+ if (!_requirements.CheckRoleRequirements(requirements, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
selector.LockRequirements(reason);
Profile = Profile?.WithAntagPreference(antag.ID, false);
@@ -697,11 +712,12 @@ private void ReloadPreview()
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
- if (Profile == null || !_prototypeManager.HasIndex(Profile.Species))
+ if (Profile == null || !_prototypeManager.HasIndex(Profile.Species))
return;
PreviewDummy = _controller.LoadProfileEntity(Profile, JobOverride, ShowClothes.Pressed);
SpriteView.SetEntity(PreviewDummy);
+ _entManager.System().SetEntityName(PreviewDummy, Profile.Name);
}
///
@@ -887,7 +903,7 @@ public void RefreshJobs()
icon.Texture = jobIcon.Icon.Frame0();
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides);
- if (!_requirements.IsAllowed(job, out var reason))
+ if (!_requirements.IsAllowed(job, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
selector.LockRequirements(reason);
}
@@ -1130,6 +1146,17 @@ protected override void Dispose(bool disposing)
_loadoutWindow?.Dispose();
_loadoutWindow = null;
+ }
+
+ protected override void EnteredTree()
+ {
+ base.EnteredTree();
+ ReloadPreview();
+ }
+
+ protected override void ExitedTree()
+ {
+ base.ExitedTree();
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
}
@@ -1190,6 +1217,11 @@ private void SetName(string newName)
{
Profile = Profile?.WithName(newName);
SetDirty();
+
+ if (!IsDirty)
+ return;
+
+ _entManager.System().SetEntityName(PreviewDummy, newName);
}
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
@@ -1198,7 +1230,7 @@ private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
SetDirty();
}
- private bool IsDirty
+ public bool IsDirty
{
get => _isDirty;
set
@@ -1521,6 +1553,19 @@ private void RandomizeName()
UpdateNameEdit();
}
+ private async void ExportImage()
+ {
+ if (_imaging)
+ return;
+
+ var dir = SpriteView.OverrideDirection ?? Direction.South;
+
+ // I tried disabling the button but it looks sorta goofy as it only takes a frame or two to save
+ _imaging = true;
+ await _entManager.System().Export(PreviewDummy, dir, includeId: false);
+ _imaging = false;
+ }
+
private async void ImportProfile()
{
if (_exporting || CharacterSlot == null || Profile == null)
diff --git a/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs b/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs
index 09bdedfd94c..11abe8c2451 100644
--- a/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs
+++ b/Content.Client/MachineLinking/UI/SignalTimerBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.MachineLinking;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Shared.Timing;
namespace Content.Client.MachineLinking.UI;
@@ -19,19 +20,14 @@ protected override void Open()
{
base.Open();
- _window = new SignalTimerWindow(this);
-
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+ _window.OnStartTimer += StartTimer;
_window.OnCurrentTextChanged += OnTextChanged;
_window.OnCurrentDelayMinutesChanged += OnDelayChanged;
_window.OnCurrentDelaySecondsChanged += OnDelayChanged;
}
- public void OnStartTimer()
+ public void StartTimer()
{
SendMessage(new SignalTimerStartMessage());
}
@@ -48,11 +44,6 @@ private void OnDelayChanged(string newDelay)
SendMessage(new SignalTimerDelayChangedMessage(_window.GetDelay()));
}
- public TimeSpan GetCurrentTime()
- {
- return _gameTiming.CurTime;
- }
-
///
/// Update the UI state based on server-sent info
///
@@ -72,11 +63,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.SetTimerStarted(cast.TimerStarted);
_window.SetHasAccess(cast.HasAccess);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
diff --git a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs
index b62595595e5..6133abfcb70 100644
--- a/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs
+++ b/Content.Client/MachineLinking/UI/SignalTimerWindow.xaml.cs
@@ -9,42 +9,44 @@ namespace Content.Client.MachineLinking.UI;
[GenerateTypedNameReferences]
public sealed partial class SignalTimerWindow : DefaultWindow
{
+ [Dependency] private readonly IGameTiming _timing = default!;
+
private const int MaxTextLength = 5;
public event Action? OnCurrentTextChanged;
public event Action? OnCurrentDelayMinutesChanged;
public event Action? OnCurrentDelaySecondsChanged;
- private readonly SignalTimerBoundUserInterface _owner;
-
private TimeSpan? _triggerTime;
private bool _timerStarted;
- public SignalTimerWindow(SignalTimerBoundUserInterface owner)
+ public event Action? OnStartTimer;
+
+ public SignalTimerWindow()
{
RobustXamlLoader.Load(this);
-
- _owner = owner;
+ IoCManager.InjectDependencies(this);
CurrentTextEdit.OnTextChanged += e => OnCurrentTextChange(e.Text);
CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text);
CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text);
- StartTimer.OnPressed += _ => OnStartTimer();
+ StartTimer.OnPressed += _ => StartTimerWeh();
}
- public void OnStartTimer()
+ private void StartTimerWeh()
{
if (!_timerStarted)
{
_timerStarted = true;
- _triggerTime = _owner.GetCurrentTime() + GetDelay();
+ _triggerTime = _timing.CurTime + GetDelay();
}
else
{
SetTimerStarted(false);
}
- _owner.OnStartTimer();
+
+ OnStartTimer?.Invoke();
}
protected override void FrameUpdate(FrameEventArgs args)
@@ -54,9 +56,9 @@ protected override void FrameUpdate(FrameEventArgs args)
if (!_timerStarted || _triggerTime == null)
return;
- if (_owner.GetCurrentTime() < _triggerTime.Value)
+ if (_timing.CurTime < _triggerTime.Value)
{
- StartTimer.Text = TextScreenSystem.TimeToString(_triggerTime.Value - _owner.GetCurrentTime());
+ StartTimer.Text = TextScreenSystem.TimeToString(_triggerTime.Value - _timing.CurTime);
}
else
{
diff --git a/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs b/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs
index f6979bf8d7b..0a87948ff62 100644
--- a/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs
+++ b/Content.Client/MagicMirror/MagicMirrorBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Humanoid.Markings;
using Content.Shared.MagicMirror;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.MagicMirror;
@@ -17,7 +18,7 @@ protected override void Open()
{
base.Open();
- _window = new();
+ _window = this.CreateWindow();
_window.OnHairSelected += tuple => SelectHair(MagicMirrorCategory.Hair, tuple.id, tuple.slot);
_window.OnHairColorChanged += args => ChangeColor(MagicMirrorCategory.Hair, args.marking, args.slot);
@@ -29,9 +30,6 @@ protected override void Open()
args => ChangeColor(MagicMirrorCategory.FacialHair, args.marking, args.slot);
_window.OnFacialHairSlotAdded += delegate () { AddSlot(MagicMirrorCategory.FacialHair); };
_window.OnFacialHairSlotRemoved += args => RemoveSlot(MagicMirrorCategory.FacialHair, args);
-
- _window.OnClose += Close;
- _window.OpenCentered();
}
private void SelectHair(MagicMirrorCategory category, string marking, int slot)
@@ -65,14 +63,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.UpdateState(data);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _window?.Dispose();
- }
}
diff --git a/Content.Client/MainMenu/MainMenu.cs b/Content.Client/MainMenu/MainMenu.cs
index 43c5bfe5674..3c709d2d15b 100644
--- a/Content.Client/MainMenu/MainMenu.cs
+++ b/Content.Client/MainMenu/MainMenu.cs
@@ -25,6 +25,9 @@ public sealed class MainScreen : Robust.Client.State.State
[Dependency] private readonly IGameController _controllerProxy = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly ILogManager _logManager = default!;
+
+ private ISawmill _sawmill = default!;
private MainMenuControl _mainMenuControl = default!;
private bool _isConnecting;
@@ -35,6 +38,8 @@ public sealed class MainScreen : Robust.Client.State.State
///
protected override void Startup()
{
+ _sawmill = _logManager.GetSawmill("mainmenu");
+
_mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager);
_userInterfaceManager.StateRoot.AddChild(_mainMenuControl);
@@ -116,7 +121,7 @@ private void TryConnect(string address)
catch (ArgumentException e)
{
_userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
- Logger.Warning(e.ToString());
+ _sawmill.Warning(e.ToString());
_netManager.ConnectFailed -= _onConnectFailed;
_setConnectingState(false);
}
diff --git a/Content.Client/Maps/GridDraggingSystem.cs b/Content.Client/Maps/GridDraggingSystem.cs
index 5e9250f0ce9..5bbf5ff5006 100644
--- a/Content.Client/Maps/GridDraggingSystem.cs
+++ b/Content.Client/Maps/GridDraggingSystem.cs
@@ -18,6 +18,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
public bool Enabled { get; set; }
@@ -62,11 +63,11 @@ private void StopDragging()
if (_dragging == null) return;
if (_lastMousePosition != null && TryComp(_dragging.Value, out TransformComponent? xform) &&
- TryComp(_dragging.Value, out var body) &&
+ TryComp(_dragging.Value, out _) &&
xform.MapID == _lastMousePosition.Value.MapId)
{
var tickTime = _gameTiming.TickPeriod;
- var distance = _lastMousePosition.Value.Position - xform.WorldPosition;
+ var distance = _lastMousePosition.Value.Position - _transformSystem.GetWorldPosition(xform);
RaiseNetworkEvent(new GridDragVelocityRequest()
{
Grid = GetNetEntity(_dragging.Value),
diff --git a/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs
index 80eca82e324..22e5bc452a0 100644
--- a/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs
+++ b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs
@@ -1,6 +1,7 @@
using JetBrains.Annotations;
using Content.Shared.MassMedia.Systems;
using Content.Shared.MassMedia.Components;
+using Robust.Client.UserInterface;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -9,8 +10,6 @@ namespace Content.Client.MassMedia.Ui;
[UsedImplicitly]
public sealed class NewsWriterBoundUserInterface : BoundUserInterface
{
- [Dependency] private readonly IGameTiming _gameTiming = default!;
-
[ViewVariables]
private NewsWriterMenu? _menu;
@@ -21,10 +20,7 @@ public NewsWriterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u
protected override void Open()
{
- _menu = new NewsWriterMenu(_gameTiming);
-
- _menu.OpenCentered();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
_menu.DeleteButtonPressed += OnDeleteButtonPressed;
@@ -32,16 +28,6 @@ protected override void Open()
SendMessage(new NewsWriterArticlesRequestMessage());
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Close();
- _menu?.Dispose();
- }
-
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
diff --git a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs
index e2d57935e3a..c059ce785af 100644
--- a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs
+++ b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs
@@ -10,17 +10,17 @@ namespace Content.Client.MassMedia.Ui;
[GenerateTypedNameReferences]
public sealed partial class NewsWriterMenu : FancyWindow
{
- private readonly IGameTiming _gameTiming;
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
private TimeSpan? _nextPublish;
public event Action? DeleteButtonPressed;
- public NewsWriterMenu(IGameTiming gameTiming)
+ public NewsWriterMenu()
{
RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
- _gameTiming = gameTiming;
ContentsContainer.RectClipContent = false;
// Customize scrollbar width and margin. This is not possible in xaml
diff --git a/Content.Client/Materials/MaterialStorageSystem.cs b/Content.Client/Materials/MaterialStorageSystem.cs
index edd07391f7b..592471d6736 100644
--- a/Content.Client/Materials/MaterialStorageSystem.cs
+++ b/Content.Client/Materials/MaterialStorageSystem.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Materials;
+using Content.Shared.Materials;
using Robust.Client.GameObjects;
namespace Content.Client.Materials;
@@ -49,7 +49,7 @@ public override bool TryInsertMaterialEntity(EntityUid user,
{
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
return false;
- _transform.DetachParentToNull(toInsert, Transform(toInsert));
+ _transform.DetachEntity(toInsert, Transform(toInsert));
return true;
}
}
diff --git a/Content.Client/Mech/Ui/MechBoundUserInterface.cs b/Content.Client/Mech/Ui/MechBoundUserInterface.cs
index 4172bdc90f1..2130a8c6099 100644
--- a/Content.Client/Mech/Ui/MechBoundUserInterface.cs
+++ b/Content.Client/Mech/Ui/MechBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Mech.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Mech.Ui;
@@ -20,9 +21,8 @@ protected override void Open()
{
base.Open();
- _menu = new(Owner);
-
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
_menu.OpenCenteredLeft();
_menu.OnRemoveButtonPressed += uid =>
@@ -60,16 +60,6 @@ public void UpdateEquipmentControls(MechBoundUiState state)
}
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (!disposing)
- return;
-
- _menu?.Close();
- }
-
public UIFragment? GetEquipmentUi(EntityUid? uid)
{
var component = EntMan.GetComponentOrNull(uid);
diff --git a/Content.Client/Mech/Ui/MechMenu.xaml.cs b/Content.Client/Mech/Ui/MechMenu.xaml.cs
index fad76488086..7ce863b7cd7 100644
--- a/Content.Client/Mech/Ui/MechMenu.xaml.cs
+++ b/Content.Client/Mech/Ui/MechMenu.xaml.cs
@@ -12,18 +12,20 @@ public sealed partial class MechMenu : FancyWindow
{
[Dependency] private readonly IEntityManager _ent = default!;
- private readonly EntityUid _mech;
+ private EntityUid _mech;
public event Action? OnRemoveButtonPressed;
- public MechMenu(EntityUid mech)
+ public MechMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
+ }
- _mech = mech;
-
- MechView.SetEntity(mech);
+ public void SetEntity(EntityUid uid)
+ {
+ MechView.SetEntity(uid);
+ _mech = uid;
}
public void UpdateMechStats()
diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs
index 39788809871..b1f239cd78e 100644
--- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs
+++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringBoundUserInterface.cs
@@ -1,4 +1,5 @@
using Content.Shared.Medical.CrewMonitoring;
+using Robust.Client.UserInterface;
namespace Content.Client.Medical.CrewMonitoring;
@@ -14,7 +15,7 @@ public CrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne
protected override void Open()
{
EntityUid? gridUid = null;
- string stationName = string.Empty;
+ var stationName = string.Empty;
if (EntMan.TryGetComponent(Owner, out var xform))
{
@@ -26,10 +27,8 @@ protected override void Open()
}
}
- _menu = new CrewMonitoringWindow(stationName, gridUid);
-
- _menu.OpenCentered();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.Set(stationName, gridUid);
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -44,13 +43,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
break;
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
index 863412e5532..0709921ae8b 100644
--- a/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
+++ b/Content.Client/Medical/CrewMonitoring/CrewMonitoringWindow.xaml.cs
@@ -23,22 +23,27 @@ namespace Content.Client.Medical.CrewMonitoring;
[GenerateTypedNameReferences]
public sealed partial class CrewMonitoringWindow : FancyWindow
{
- private readonly IEntityManager _entManager;
- private readonly IPrototypeManager _prototypeManager;
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly SpriteSystem _spriteSystem;
private NetEntity? _trackedEntity;
private bool _tryToScrollToListFocus;
private Texture? _blipTexture;
- public CrewMonitoringWindow(string stationName, EntityUid? mapUid)
+ public CrewMonitoringWindow()
{
RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
- _entManager = IoCManager.Resolve();
- _prototypeManager = IoCManager.Resolve();
_spriteSystem = _entManager.System();
+ NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
+
+ }
+
+ public void Set(string stationName, EntityUid? mapUid)
+ {
_blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
if (_entManager.TryGetComponent(mapUid, out var xform))
@@ -49,8 +54,6 @@ public CrewMonitoringWindow(string stationName, EntityUid? mapUid)
StationName.AddStyleClass("LabelBig");
StationName.Text = stationName;
-
- NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
NavMap.ForceNavMapUpdate();
}
@@ -254,7 +257,7 @@ private void PopulateDepartmentList(IEnumerable departmentSens
mainContainer.AddChild(jobContainer);
// Job icon
- if (_prototypeManager.TryIndex(sensor.JobIcon, out var proto))
+ if (_prototypeManager.TryIndex(sensor.JobIcon, out var proto))
{
var jobIcon = new TextureRect()
{
diff --git a/Content.Client/MouseRotator/MouseRotatorSystem.cs b/Content.Client/MouseRotator/MouseRotatorSystem.cs
index ce174c6144c..18d60d9a7b9 100644
--- a/Content.Client/MouseRotator/MouseRotatorSystem.cs
+++ b/Content.Client/MouseRotator/MouseRotatorSystem.cs
@@ -2,7 +2,6 @@
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
-using Robust.Client.Replays.Loading;
using Robust.Shared.Map;
using Robust.Shared.Timing;
@@ -46,13 +45,19 @@ public override void Update(float frameTime)
// only raise event if the cardinal direction has changed
if (rotator.Simple4DirMode)
{
- var angleDir = angle.GetCardinalDir();
- if (angleDir == curRot.GetCardinalDir())
+ var eyeRot = _eye.CurrentEye.Rotation; // camera rotation
+ var angleDir = (angle + eyeRot).GetCardinalDir(); // apply GetCardinalDir in the camera frame, not in the world frame
+ if (angleDir == (curRot + eyeRot).GetCardinalDir())
return;
- RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
+ var rotation = angleDir.ToAngle() - eyeRot; // convert back to world frame
+ if (rotation >= Math.PI) // convert to [-PI, +PI)
+ rotation -= 2 * Math.PI;
+ else if (rotation < -Math.PI)
+ rotation += 2 * Math.PI;
+ RaisePredictiveEvent(new RequestMouseRotatorRotationEvent
{
- Direction = angleDir,
+ Rotation = rotation
});
return;
diff --git a/Content.Client/Movement/Systems/ContentEyeSystem.cs b/Content.Client/Movement/Systems/ContentEyeSystem.cs
index 182ac92ae05..9fbd4b5c37d 100644
--- a/Content.Client/Movement/Systems/ContentEyeSystem.cs
+++ b/Content.Client/Movement/Systems/ContentEyeSystem.cs
@@ -9,7 +9,7 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
- public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, ContentEyeComponent? content = null)
+ public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, bool scalePvs, ContentEyeComponent? content = null)
{
if (!Resolve(uid, ref content, false))
return;
@@ -19,6 +19,14 @@ public void RequestZoom(EntityUid uid, Vector2 zoom, bool ignoreLimit, ContentEy
TargetZoom = zoom,
IgnoreLimit = ignoreLimit,
});
+
+ if (scalePvs)
+ RequestPvsScale(Math.Max(zoom.X, zoom.Y));
+ }
+
+ public void RequestPvsScale(float scale)
+ {
+ RaiseNetworkEvent(new RequestPvsScaleEvent(scale));
}
public void RequestToggleFov()
diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs
index b7f5e48821f..2954140d792 100644
--- a/Content.Client/Movement/Systems/JetpackSystem.cs
+++ b/Content.Client/Movement/Systems/JetpackSystem.cs
@@ -15,6 +15,7 @@ public sealed class JetpackSystem : SharedJetpackSystem
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ClothingSystem _clothing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!;
public override void Initialize()
{
@@ -63,21 +64,21 @@ public override void Update(float frameTime)
private void CreateParticles(EntityUid uid)
{
+ var uidXform = Transform(uid);
// Don't show particles unless the user is moving.
- if (Container.TryGetContainingContainer(uid, out var container) &&
+ if (Container.TryGetContainingContainer((uid, uidXform, null), out var container) &&
TryComp(container.Owner, out var body) &&
body.LinearVelocity.LengthSquared() < 1f)
{
return;
}
- var uidXform = Transform(uid);
var coordinates = uidXform.Coordinates;
- var gridUid = coordinates.GetGridUid(EntityManager);
+ var gridUid = _transform.GetGrid(coordinates);
if (TryComp(gridUid, out var grid))
{
- coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform)));
+ coordinates = new EntityCoordinates(gridUid.Value, _mapSystem.WorldToLocal(gridUid.Value, grid, _transform.ToMapCoordinates(coordinates).Position));
}
else if (uidXform.MapUid != null)
{
diff --git a/Content.Client/Movement/Systems/SpriteMovementSystem.cs b/Content.Client/Movement/Systems/SpriteMovementSystem.cs
index 313683855d0..b6203536896 100644
--- a/Content.Client/Movement/Systems/SpriteMovementSystem.cs
+++ b/Content.Client/Movement/Systems/SpriteMovementSystem.cs
@@ -28,7 +28,7 @@ private void OnSpriteMoveInput(EntityUid uid, SpriteMovementComponent component,
return;
var oldMoving = (SharedMoverController.GetNormalizedMovement(args.OldMovement) & MoveButtons.AnyDirection) != MoveButtons.None;
- var moving = (SharedMoverController.GetNormalizedMovement(args.Component.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
+ var moving = (SharedMoverController.GetNormalizedMovement(args.Entity.Comp.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
if (oldMoving == moving || !_spriteQuery.TryGetComponent(uid, out var sprite))
return;
diff --git a/Content.Client/NPC/HTN/HTNOverlay.cs b/Content.Client/NPC/HTN/HTNOverlay.cs
index ad173f3b29c..2cb02f10864 100644
--- a/Content.Client/NPC/HTN/HTNOverlay.cs
+++ b/Content.Client/NPC/HTN/HTNOverlay.cs
@@ -9,6 +9,7 @@ public sealed class HTNOverlay : Overlay
{
private readonly IEntityManager _entManager = default!;
private readonly Font _font = default!;
+ private readonly SharedTransformSystem _transformSystem;
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
@@ -16,6 +17,7 @@ public HTNOverlay(IEntityManager entManager, IResourceCache resourceCache)
{
_entManager = entManager;
_font = new VectorFont(resourceCache.GetResource("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
+ _transformSystem = _entManager.System();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -30,7 +32,7 @@ protected override void Draw(in OverlayDrawArgs args)
if (string.IsNullOrEmpty(comp.DebugText) || xform.MapID != args.MapId)
continue;
- var worldPos = xform.WorldPosition;
+ var worldPos = _transformSystem.GetWorldPosition(xform);
if (!args.WorldAABB.Contains(worldPos))
continue;
diff --git a/Content.Client/NPC/NPCSteeringSystem.cs b/Content.Client/NPC/NPCSteeringSystem.cs
index bb2145bce0c..eda3d74cf71 100644
--- a/Content.Client/NPC/NPCSteeringSystem.cs
+++ b/Content.Client/NPC/NPCSteeringSystem.cs
@@ -81,10 +81,12 @@ public sealed class NPCSteeringOverlay : Overlay
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly IEntityManager _entManager;
+ private readonly SharedTransformSystem _transformSystem;
public NPCSteeringOverlay(IEntityManager entManager)
{
_entManager = entManager;
+ _transformSystem = _entManager.System();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -96,7 +98,7 @@ protected override void Draw(in OverlayDrawArgs args)
continue;
}
- var (worldPos, worldRot) = xform.GetWorldPositionRotation();
+ var (worldPos, worldRot) = _transformSystem.GetWorldPositionRotation(xform);
if (!args.WorldAABB.Contains(worldPos))
continue;
diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs
index d3ae5091528..0c72a8f99ff 100644
--- a/Content.Client/NPC/PathfindingSystem.cs
+++ b/Content.Client/NPC/PathfindingSystem.cs
@@ -203,7 +203,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle)
if (found || !_system.Breadcrumbs.TryGetValue(netGrid, out var crumbs) || !xformQuery.TryGetComponent(grid, out var gridXform))
continue;
- var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
var localAABB = invWorldMatrix.TransformBox(aabb.Enlarged(float.Epsilon - SharedPathfindingSystem.ChunkSize));
foreach (var chunk in crumbs)
@@ -287,7 +287,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle)
return;
}
- var invGridMatrix = gridXform.InvWorldMatrix;
+ var invGridMatrix = _transformSystem.GetInvWorldMatrix(gridXform);
DebugPathPoly? nearest = null;
foreach (var poly in tile)
@@ -359,7 +359,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
continue;
}
- var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
worldHandle.SetTransform(worldMatrix);
var localAABB = invWorldMatrix.TransformBox(aabb);
@@ -419,7 +419,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
!xformQuery.TryGetComponent(grid, out var gridXform))
continue;
- var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
worldHandle.SetTransform(worldMatrix);
var localAABB = invWorldMatrix.TransformBox(aabb);
@@ -458,7 +458,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
!xformQuery.TryGetComponent(grid, out var gridXform))
continue;
- var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
worldHandle.SetTransform(worldMatrix);
var localAABB = invMatrix.TransformBox(aabb);
@@ -483,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, _transformSystem);
+ var neighborMap = _transformSystem.ToMapCoordinates(_entManager.GetCoordinates(neighborPoly));
if (neighborMap.MapId != args.MapId)
continue;
@@ -517,7 +517,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
!xformQuery.TryGetComponent(grid, out var gridXform))
continue;
- var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform);
worldHandle.SetTransform(worldMatrix);
var localAABB = invWorldMatrix.TransformBox(args.WorldBounds);
@@ -544,7 +544,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
if (!_entManager.TryGetComponent(_entManager.GetEntity(node.GraphUid), out var graphXform))
continue;
- worldHandle.SetTransform(graphXform.WorldMatrix);
+ worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform));
worldHandle.DrawRect(node.Box, Color.Orange.WithAlpha(0.10f));
}
}
@@ -568,7 +568,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle)
continue;
matrix = graph;
- worldHandle.SetTransform(graphXform.WorldMatrix);
+ worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform));
}
worldHandle.DrawRect(node.Box, new Color(0f, cost / highestGScore, 1f - (cost / highestGScore), 0.10f));
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs
index 80c98f143b9..f85220a9266 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Client.NetworkConfigurator.Systems;
using Content.Shared.DeviceNetwork;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.NetworkConfigurator;
@@ -35,14 +36,12 @@ protected override void Open()
switch (UiKey)
{
case NetworkConfiguratorUiKey.List:
- _listMenu = new NetworkConfiguratorListMenu(this);
- _listMenu.OnClose += Close;
+ _listMenu = this.CreateWindow();
_listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed();
- _listMenu.OpenCenteredRight();
+ _listMenu.OnRemoveAddress += OnRemoveButtonPressed;
break;
case NetworkConfiguratorUiKey.Configure:
- _configurationMenu = new NetworkConfiguratorConfigurationMenu();
- _configurationMenu.OnClose += Close;
+ _configurationMenu = this.CreateWindow();
_configurationMenu.Set.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Set);
_configurationMenu.Add.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Add);
//_configurationMenu.Edit.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Edit);
@@ -50,12 +49,24 @@ protected override void Open()
_configurationMenu.Copy.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Copy);
_configurationMenu.Show.OnPressed += OnShowPressed;
_configurationMenu.Show.Pressed = _netConfig.ConfiguredListIsTracked(Owner);
- _configurationMenu.OpenCentered();
+ _configurationMenu.OnRemoveAddress += OnRemoveButtonPressed;
break;
case NetworkConfiguratorUiKey.Link:
- _linkMenu = new NetworkConfiguratorLinkMenu(this);
- _linkMenu.OnClose += Close;
- _linkMenu.OpenCentered();
+ _linkMenu = this.CreateWindow();
+ _linkMenu.OnLinkDefaults += args =>
+ {
+ SendMessage(new NetworkConfiguratorLinksSaveMessage(args));
+ };
+
+ _linkMenu.OnToggleLink += (left, right) =>
+ {
+ SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right));
+ };
+
+ _linkMenu.OnClearLinks += () =>
+ {
+ SendMessage(new NetworkConfiguratorClearLinksMessage());
+ };
break;
}
}
@@ -83,16 +94,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
}
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
-
- _linkMenu?.Dispose();
- _listMenu?.Dispose();
- _configurationMenu?.Dispose();
- }
-
private void OnClearButtonPressed()
{
SendMessage(new NetworkConfiguratorClearDevicesMessage());
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs
index 19d04cd3464..fcd2f759187 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml.cs
@@ -9,17 +9,23 @@ namespace Content.Client.NetworkConfigurator;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorConfigurationMenu : FancyWindow
{
+ public event Action? OnRemoveAddress;
+
public NetworkConfiguratorConfigurationMenu()
{
RobustXamlLoader.Load(this);
Clear.StyleClasses.Add(StyleBase.ButtonOpenLeft);
Clear.StyleClasses.Add(StyleNano.StyleClassButtonColorRed);
+ DeviceList.OnRemoveAddress += args =>
+ {
+ OnRemoveAddress?.Invoke(args);
+ };
}
public void UpdateState(DeviceListUserInterfaceState state)
{
- DeviceList.UpdateState(null, state.DeviceList);
+ DeviceList.UpdateState(state.DeviceList, false);
Count.Text = Loc.GetString("network-configurator-ui-count-label", ("count", state.DeviceList.Count));
}
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs
index 8cfa97dc6c2..e75c60058cb 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorDeviceList.xaml.cs
@@ -7,17 +7,19 @@ namespace Content.Client.NetworkConfigurator;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorDeviceList : ScrollContainer
{
- public void UpdateState(NetworkConfiguratorBoundUserInterface? ui, HashSet<(string address, string name)> devices)
+ public event Action? OnRemoveAddress;
+
+ public void UpdateState(HashSet<(string address, string name)> devices, bool ui)
{
DeviceList.RemoveAllChildren();
foreach (var device in devices)
{
- DeviceList.AddChild(BuildDeviceListRow(ui, device));
+ DeviceList.AddChild(BuildDeviceListRow(device, ui));
}
}
- private static BoxContainer BuildDeviceListRow(NetworkConfiguratorBoundUserInterface? ui, (string address, string name) savedDevice)
+ private BoxContainer BuildDeviceListRow((string address, string name) savedDevice, bool ui)
{
var row = new BoxContainer()
{
@@ -48,10 +50,10 @@ private static BoxContainer BuildDeviceListRow(NetworkConfiguratorBoundUserInter
row.AddChild(name);
row.AddChild(address);
- if (ui != null)
+ if (ui)
{
row.AddChild(removeButton);
- removeButton.OnPressed += _ => ui.OnRemoveButtonPressed(savedDevice.address);
+ removeButton.OnPressed += _ => OnRemoveAddress?.Invoke(savedDevice.address);
}
return row;
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs
index c04b42f249b..8cdffd16af6 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs
@@ -18,20 +18,20 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
private readonly LinksRender _links;
-
private readonly List _sources = new();
private readonly List _sinks = new();
- private readonly NetworkConfiguratorBoundUserInterface _userInterface;
-
private (ButtonPosition position, string id, int index)? _selectedButton;
private List<(string left, string right)>? _defaults;
- public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInterface)
+ public event Action? OnClearLinks;
+ public event Action? OnToggleLink;
+ public event Action>? OnLinkDefaults;
+
+ public NetworkConfiguratorLinkMenu()
{
- _userInterface = userInterface;
RobustXamlLoader.Load(this);
var footerStyleBox = new StyleBoxFlat()
@@ -52,7 +52,7 @@ public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInt
ButtonOk.OnPressed += _ => Close();
ButtonLinkDefault.OnPressed += _ => LinkDefaults();
- ButtonClear.OnPressed += _ => _userInterface.SendMessage(new NetworkConfiguratorClearLinksMessage());
+ ButtonClear.OnPressed += _ => OnClearLinks?.Invoke();
}
public void UpdateState(DeviceLinkUserInterfaceState linkState)
@@ -98,7 +98,7 @@ private void LinkDefaults()
if (_defaults == default)
return;
- _userInterface.SendMessage(new NetworkConfiguratorLinksSaveMessage(_defaults));
+ OnLinkDefaults?.Invoke(_defaults);
}
private Button CreateButton(ButtonPosition position, string name, string description, string id, int index)
@@ -138,7 +138,7 @@ private void OnButtonPressed(BaseButton.ButtonEventArgs args, ButtonPosition pos
var left = _selectedButton.Value.position == ButtonPosition.Left ? _selectedButton.Value.id : id;
var right = _selectedButton.Value.position == ButtonPosition.Left ? id : _selectedButton.Value.id;
- _userInterface.SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right));
+ OnToggleLink?.Invoke(left, right);
args.Button.Pressed = false;
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkOverlay.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkOverlay.cs
index 4fcdada8684..19da5aa9594 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkOverlay.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkOverlay.cs
@@ -12,6 +12,7 @@ public sealed class NetworkConfiguratorLinkOverlay : Overlay
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private readonly DeviceListSystem _deviceListSystem;
+ private readonly SharedTransformSystem _transformSystem;
public Dictionary Colors = new();
public EntityUid? Action;
@@ -23,6 +24,7 @@ public NetworkConfiguratorLinkOverlay()
IoCManager.InjectDependencies(this);
_deviceListSystem = _entityManager.System();
+ _transformSystem = _entityManager.System();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -66,7 +68,7 @@ protected override void Draw(in OverlayDrawArgs args)
continue;
}
- args.WorldHandle.DrawLine(sourceTransform.WorldPosition, linkTransform.WorldPosition, Colors[uid]);
+ args.WorldHandle.DrawLine(_transformSystem.GetWorldPosition(sourceTransform), _transformSystem.GetWorldPosition(linkTransform), Colors[uid]);
}
}
}
diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs
index fb4aec1974b..6294facaeed 100644
--- a/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs
+++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorListMenu.xaml.cs
@@ -9,17 +9,20 @@ namespace Content.Client.NetworkConfigurator;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorListMenu : FancyWindow
{
- private readonly NetworkConfiguratorBoundUserInterface _ui;
- public NetworkConfiguratorListMenu(NetworkConfiguratorBoundUserInterface ui)
+ public event Action? OnRemoveAddress;
+
+ public NetworkConfiguratorListMenu()
{
RobustXamlLoader.Load(this);
-
- _ui = ui;
+ DeviceList.OnRemoveAddress += args =>
+ {
+ OnRemoveAddress?.Invoke(args);
+ };
}
public void UpdateState(NetworkConfiguratorUserInterfaceState state)
{
DeviceCountLabel.Text = Loc.GetString("network-configurator-ui-count-label", ("count", state.DeviceList.Count));
- DeviceList.UpdateState(_ui, state.DeviceList);
+ DeviceList.UpdateState(state.DeviceList, true);
}
}
diff --git a/Content.Client/Ninja/Systems/ItemCreatorSystem.cs b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs
new file mode 100644
index 00000000000..9ab62cc12db
--- /dev/null
+++ b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs
@@ -0,0 +1,5 @@
+using Content.Shared.Ninja.Systems;
+
+namespace Content.Client.Ninja.Systems;
+
+public sealed class ItemCreatorSystem : SharedItemCreatorSystem;
diff --git a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs
index 7758c3d7e2b..5b07b1588fd 100644
--- a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs
+++ b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs
@@ -2,9 +2,4 @@
namespace Content.Client.Ninja.Systems;
-///
-/// Does nothing special, only exists to provide a client implementation.
-///
-public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem
-{
-}
+public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem;
diff --git a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs
index fde1801b37d..852ea8af46e 100644
--- a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs
+++ b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs
@@ -1,24 +1,5 @@
-using Content.Shared.Clothing.EntitySystems;
-using Content.Shared.Ninja.Components;
using Content.Shared.Ninja.Systems;
namespace Content.Client.Ninja.Systems;
-///
-/// Disables cloak prediction since client has no knowledge of battery power.
-/// Cloak will still be enabled after server tells it.
-///
-public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
-{
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(OnAttemptStealth);
- }
-
- private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args)
- {
- args.Cancel();
- }
-}
+public sealed class NinjaSuitSystem : SharedNinjaSuitSystem;
diff --git a/Content.Client/Ninja/Systems/NinjaSystem.cs b/Content.Client/Ninja/Systems/NinjaSystem.cs
index aa2fa2047f1..958dc6a5d9a 100644
--- a/Content.Client/Ninja/Systems/NinjaSystem.cs
+++ b/Content.Client/Ninja/Systems/NinjaSystem.cs
@@ -2,11 +2,4 @@
namespace Content.Client.Ninja.Systems;
-///
-/// Currently does nothing special clientside.
-/// All functionality is in shared and server.
-/// Only exists to prevent crashing.
-///
-public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
-{
-}
+public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem;
diff --git a/Content.Client/Ninja/Systems/SpiderChargeSystem.cs b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs
new file mode 100644
index 00000000000..b107fd3867d
--- /dev/null
+++ b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs
@@ -0,0 +1,5 @@
+using Content.Shared.Ninja.Systems;
+
+namespace Content.Client.Ninja.Systems;
+
+public sealed class SpiderChargeSystem : SharedSpiderChargeSystem;
diff --git a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
index 4e8a4a10ca6..b0bdf8ed87d 100644
--- a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
+++ b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs
@@ -20,6 +20,7 @@ public sealed class NodeVisualizationOverlay : Overlay
private readonly IMapManager _mapManager;
private readonly IInputManager _inputManager;
private readonly IEntityManager _entityManager;
+ private readonly SharedTransformSystem _transformSystem;
private readonly Dictionary<(int, int), NodeRenderData> _nodeIndex = new();
private readonly Dictionary>> _gridIndex = new ();
@@ -46,6 +47,7 @@ public NodeVisualizationOverlay(
_mapManager = mapManager;
_inputManager = inputManager;
_entityManager = entityManager;
+ _transformSystem = _entityManager.System();
_font = cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12);
}
@@ -146,7 +148,7 @@ private void DrawWorld(in OverlayDrawArgs overlayDrawArgs)
foreach (var (gridId, gridDict) in _gridIndex)
{
var grid = _entityManager.GetComponent(gridId);
- var (_, _, worldMatrix, invMatrix) = _entityManager.GetComponent(gridId).GetWorldPositionRotationMatrixWithInv();
+ var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridId);
var lCursorBox = invMatrix.TransformBox(cursorBox);
foreach (var (pos, list) in gridDict)
diff --git a/Content.Client/Nuke/NukeBoundUserInterface.cs b/Content.Client/Nuke/NukeBoundUserInterface.cs
index 59fbc5b319b..2e150423734 100644
--- a/Content.Client/Nuke/NukeBoundUserInterface.cs
+++ b/Content.Client/Nuke/NukeBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Nuke;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Nuke
{
@@ -11,15 +12,13 @@ public sealed class NukeBoundUserInterface : BoundUserInterface
[ViewVariables]
private NukeMenu? _menu;
- public NukeBoundUserInterface([NotNull] EntityUid owner, [NotNull] Enum uiKey) : base(owner, uiKey)
+ public NukeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
- _menu = new NukeMenu();
- _menu.OpenCentered();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
_menu.OnKeypadButtonPressed += i =>
{
@@ -62,15 +61,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
break;
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Close();
- _menu?.Dispose();
- }
}
}
diff --git a/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs b/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs
index ec055b3240c..ad4f1a75d47 100644
--- a/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs
+++ b/Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Chat;
using Content.Shared.NukeOps;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
using Robust.Shared.Timing;
@@ -11,8 +12,6 @@ namespace Content.Client.NukeOps;
public sealed class WarDeclaratorBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly ILocalizationManager _localizationManager = default!;
[ViewVariables]
private WarDeclaratorWindow? _window;
@@ -23,13 +22,7 @@ protected override void Open()
{
base.Open();
- _window = new WarDeclaratorWindow(_gameTiming, _localizationManager);
- if (State != null)
- UpdateState(State);
-
- _window.OpenCentered();
-
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.OnActivated += OnWarDeclaratorActivated;
}
@@ -42,13 +35,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(cast);
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- _window?.Dispose();
- }
-
private void OnWarDeclaratorActivated(string message)
{
var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
diff --git a/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs b/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs
index b4a3f1c7fa5..e191e821c22 100644
--- a/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs
+++ b/Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs
@@ -11,7 +11,8 @@ namespace Content.Client.NukeOps;
[GenerateTypedNameReferences]
public sealed partial class WarDeclaratorWindow : FancyWindow
{
- private readonly IGameTiming _gameTiming;
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly ILocalizationManager _localizationManager = default!;
public event Action? OnActivated;
@@ -19,15 +20,14 @@ public sealed partial class WarDeclaratorWindow : FancyWindow
private TimeSpan _shuttleDisabledTime;
private WarConditionStatus _status;
- public WarDeclaratorWindow(IGameTiming gameTiming, ILocalizationManager localizationManager)
+ public WarDeclaratorWindow()
{
RobustXamlLoader.Load(this);
-
- _gameTiming = gameTiming;
+ IoCManager.InjectDependencies(this);
WarButton.OnPressed += (_) => OnActivated?.Invoke(Rope.Collapse(MessageEdit.TextRope));
- MessageEdit.Placeholder = new Rope.Leaf(localizationManager.GetString("war-declarator-message-placeholder"));
+ MessageEdit.Placeholder = new Rope.Leaf(_localizationManager.GetString("war-declarator-message-placeholder"));
}
protected override void FrameUpdate(FrameEventArgs args)
diff --git a/Content.Client/Outline/InteractionOutlineSystem.cs b/Content.Client/Outline/InteractionOutlineSystem.cs
index 3dbbafbcaa3..40cb5dfd4a6 100644
--- a/Content.Client/Outline/InteractionOutlineSystem.cs
+++ b/Content.Client/Outline/InteractionOutlineSystem.cs
@@ -110,11 +110,15 @@ public override void FrameUpdate(float frameTime)
&& _inputManager.MouseScreenPosition.IsValid)
{
var mousePosWorld = vp.PixelToMap(_inputManager.MouseScreenPosition.Position);
- entityToClick = screen.GetClickedEntity(mousePosWorld);
if (vp is ScalingViewport svp)
{
renderScale = svp.CurrentRenderScale;
+ entityToClick = screen.GetClickedEntity(mousePosWorld, svp.Eye);
+ }
+ else
+ {
+ entityToClick = screen.GetClickedEntity(mousePosWorld);
}
}
else if (_uiManager.CurrentlyHovered is EntityMenuElement element)
diff --git a/Content.Client/Outline/TargetOutlineSystem.cs b/Content.Client/Outline/TargetOutlineSystem.cs
index df57578b1f5..c69987fe77c 100644
--- a/Content.Client/Outline/TargetOutlineSystem.cs
+++ b/Content.Client/Outline/TargetOutlineSystem.cs
@@ -23,6 +23,7 @@ public sealed class TargetOutlineSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
private bool _enabled = false;
@@ -165,8 +166,8 @@ private void HighlightTargets()
valid = _interactionSystem.InRangeUnobstructed(player, entity, Range);
else if (Range >= 0)
{
- var origin = Transform(player).WorldPosition;
- var target = Transform(entity).WorldPosition;
+ var origin = _transformSystem.GetWorldPosition(player);
+ var target = _transformSystem.GetWorldPosition(entity);
valid = (origin - target).LengthSquared() <= Range;
}
diff --git a/Content.Client/Overlays/EntityHealthBarOverlay.cs b/Content.Client/Overlays/EntityHealthBarOverlay.cs
index 4f928437395..193635bda7b 100644
--- a/Content.Client/Overlays/EntityHealthBarOverlay.cs
+++ b/Content.Client/Overlays/EntityHealthBarOverlay.cs
@@ -33,7 +33,7 @@ public sealed class EntityHealthBarOverlay : Overlay
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
public HashSet DamageContainers = new();
- public ProtoId? StatusIcon;
+ public ProtoId? StatusIcon;
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
{
diff --git a/Content.Client/Overlays/ShowHealthIconsSystem.cs b/Content.Client/Overlays/ShowHealthIconsSystem.cs
index d8af91482b3..8c22c78f17c 100644
--- a/Content.Client/Overlays/ShowHealthIconsSystem.cs
+++ b/Content.Client/Overlays/ShowHealthIconsSystem.cs
@@ -53,17 +53,17 @@ private void OnGetStatusIconsEvent(Entity entity, ref GetSt
args.StatusIcons.AddRange(healthIcons);
}
- private IReadOnlyList DecideHealthIcons(Entity entity)
+ private IReadOnlyList DecideHealthIcons(Entity entity)
{
var damageableComponent = entity.Comp;
if (damageableComponent.DamageContainerID == null ||
!DamageContainers.Contains(damageableComponent.DamageContainerID))
{
- return Array.Empty();
+ return Array.Empty();
}
- var result = new List();
+ 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")
diff --git a/Content.Client/Overlays/ShowJobIconsSystem.cs b/Content.Client/Overlays/ShowJobIconsSystem.cs
index a6d4f70af6b..e5ba9b813f0 100644
--- a/Content.Client/Overlays/ShowJobIconsSystem.cs
+++ b/Content.Client/Overlays/ShowJobIconsSystem.cs
@@ -13,7 +13,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem]
+ [ValidatePrototypeId]
private const string JobIconForNoId = "JobIconNoId";
public override void Initialize()
@@ -52,7 +52,7 @@ private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref Get
}
}
- if (_prototype.TryIndex(iconId, out var iconPrototype))
+ if (_prototype.TryIndex(iconId, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
else
Log.Error($"Invalid job icon prototype: {iconPrototype}");
diff --git a/Content.Client/Overlays/ShowSyndicateIconsSystem.cs b/Content.Client/Overlays/ShowSyndicateIconsSystem.cs
index 782178a29db..9d4599b8233 100644
--- a/Content.Client/Overlays/ShowSyndicateIconsSystem.cs
+++ b/Content.Client/Overlays/ShowSyndicateIconsSystem.cs
@@ -22,7 +22,7 @@ private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent compone
if (!IsActive)
return;
- if (_prototype.TryIndex(component.SyndStatusIcon, out var iconPrototype))
+ if (_prototype.TryIndex(component.SyndStatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
}
}
diff --git a/Content.Client/PDA/PdaBoundUserInterface.cs b/Content.Client/PDA/PdaBoundUserInterface.cs
index f8f4c67076c..37ce9c4280f 100644
--- a/Content.Client/PDA/PdaBoundUserInterface.cs
+++ b/Content.Client/PDA/PdaBoundUserInterface.cs
@@ -24,14 +24,13 @@ protected override void Open()
if (_menu == null)
CreateMenu();
-
- _menu?.OpenCenteredLeft();
}
private void CreateMenu()
{
- _menu = new PdaMenu();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.OpenCenteredLeft();
+
_menu.FlashLightToggleButton.OnToggled += _ =>
{
SendMessage(new PdaToggleFlashlightMessage());
@@ -96,7 +95,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
_menu?.UpdateState(updateState);
}
-
protected override void AttachCartridgeUI(Control cartridgeUIFragment, string? title)
{
_menu?.ProgramView.AddChild(cartridgeUIFragment);
@@ -118,15 +116,6 @@ protected override void UpdateAvailablePrograms(List<(EntityUid, CartridgeCompon
_menu?.UpdateAvailablePrograms(programs);
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Dispose();
- }
-
private PdaBorderColorComponent? GetBorderColorComponent()
{
return EntMan.GetComponentOrNull(Owner);
diff --git a/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs b/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs
index a0688523f1e..170a296ac2e 100644
--- a/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs
+++ b/Content.Client/PDA/Ringer/RingerBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.PDA;
using Content.Shared.PDA.Ringer;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
using Robust.Shared.Timing;
namespace Content.Client.PDA.Ringer
@@ -18,9 +19,8 @@ public RingerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey
protected override void Open()
{
base.Open();
- _menu = new RingtoneMenu();
+ _menu = this.CreateWindow();
_menu.OpenToLeft();
- _menu.OnClose += Close;
_menu.TestRingerButton.OnPressed += _ =>
{
diff --git a/Content.Client/Paper/EnvelopeSystem.cs b/Content.Client/Paper/EnvelopeSystem.cs
new file mode 100644
index 00000000000..f405ed15188
--- /dev/null
+++ b/Content.Client/Paper/EnvelopeSystem.cs
@@ -0,0 +1,35 @@
+using Content.Shared.Paper;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Paper;
+
+public sealed class EnvelopeSystem : VisualizerSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnAfterAutoHandleState);
+ }
+
+ private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ UpdateAppearance(ent);
+ }
+
+ private void UpdateAppearance(Entity ent, SpriteComponent? sprite = null)
+ {
+ if (!Resolve(ent.Owner, ref sprite))
+ return;
+
+ sprite.LayerSetVisible(EnvelopeVisualLayers.Open, ent.Comp.State == EnvelopeComponent.EnvelopeState.Open);
+ sprite.LayerSetVisible(EnvelopeVisualLayers.Sealed, ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed);
+ sprite.LayerSetVisible(EnvelopeVisualLayers.Torn, ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn);
+ }
+
+ public enum EnvelopeVisualLayers : byte
+ {
+ Open,
+ Sealed,
+ Torn
+ }
+}
diff --git a/Content.Client/Paper/PaperComponent.cs b/Content.Client/Paper/PaperComponent.cs
deleted file mode 100644
index 1dc827bf7e6..00000000000
--- a/Content.Client/Paper/PaperComponent.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using Content.Shared.Paper;
-
-namespace Content.Client.Paper;
-
-[RegisterComponent]
-public sealed partial class PaperComponent : SharedPaperComponent;
diff --git a/Content.Client/Paper/UI/PaperBoundUserInterface.cs b/Content.Client/Paper/UI/PaperBoundUserInterface.cs
index 4b0ac868f01..63645bc01e9 100644
--- a/Content.Client/Paper/UI/PaperBoundUserInterface.cs
+++ b/Content.Client/Paper/UI/PaperBoundUserInterface.cs
@@ -1,7 +1,8 @@
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Utility;
-using static Content.Shared.Paper.SharedPaperComponent;
+using static Content.Shared.Paper.PaperComponent;
namespace Content.Client.Paper.UI;
@@ -19,16 +20,13 @@ protected override void Open()
{
base.Open();
- _window = new PaperWindow();
- _window.OnClose += Close;
- _window.OnSaved += Input_OnTextEntered;
+ _window = this.CreateWindow();
+ _window.OnSaved += InputOnTextEntered;
if (EntMan.TryGetComponent(Owner, out var visuals))
{
_window.InitVisuals(Owner, visuals);
}
-
- _window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -37,7 +35,7 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.Populate((PaperBoundUserInterfaceState) state);
}
- private void Input_OnTextEntered(string text)
+ private void InputOnTextEntered(string text)
{
SendMessage(new PaperInputTextMessage(text));
@@ -47,11 +45,4 @@ private void Input_OnTextEntered(string text)
_window.Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top);
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _window?.Dispose();
- }
}
diff --git a/Content.Client/Paper/UI/PaperSystem.cs b/Content.Client/Paper/UI/PaperVisualizerSystem.cs
similarity index 82%
rename from Content.Client/Paper/UI/PaperSystem.cs
rename to Content.Client/Paper/UI/PaperVisualizerSystem.cs
index f75d04a26bd..a0d05736adb 100644
--- a/Content.Client/Paper/UI/PaperSystem.cs
+++ b/Content.Client/Paper/UI/PaperVisualizerSystem.cs
@@ -1,10 +1,10 @@
using Robust.Client.GameObjects;
-using static Content.Shared.Paper.SharedPaperComponent;
+using static Content.Shared.Paper.PaperComponent;
-namespace Content.Client.Paper;
+namespace Content.Client.Paper.UI;
-public sealed class PaperSystem : VisualizerSystem
+public sealed class PaperVisualizerSystem : VisualizerSystem
{
protected override void OnAppearanceChange(EntityUid uid, PaperVisualsComponent component, ref AppearanceChangeEvent args)
{
diff --git a/Content.Client/Paper/UI/PaperVisualsComponent.cs b/Content.Client/Paper/UI/PaperVisualsComponent.cs
index 95040e77e65..f0dee3cf907 100644
--- a/Content.Client/Paper/UI/PaperVisualsComponent.cs
+++ b/Content.Client/Paper/UI/PaperVisualsComponent.cs
@@ -1,6 +1,6 @@
using System.Numerics;
-namespace Content.Client.Paper;
+namespace Content.Client.Paper.UI;
[RegisterComponent]
public sealed partial class PaperVisualsComponent : Component
diff --git a/Content.Client/Paper/UI/PaperWindow.xaml.cs b/Content.Client/Paper/UI/PaperWindow.xaml.cs
index 7a5fd652643..81b831068c3 100644
--- a/Content.Client/Paper/UI/PaperWindow.xaml.cs
+++ b/Content.Client/Paper/UI/PaperWindow.xaml.cs
@@ -17,6 +17,7 @@ namespace Content.Client.Paper.UI
public sealed partial class PaperWindow : BaseWindow
{
[Dependency] private readonly IInputManager _inputManager = default!;
+ [Dependency] private readonly IResourceCache _resCache = default!;
private static Color DefaultTextColor = new(25, 25, 25);
@@ -85,11 +86,10 @@ public void InitVisuals(EntityUid entity, PaperVisualsComponent visuals)
// Randomize the placement of any stamps based on the entity UID
// so that there's some variety in different papers.
StampDisplay.PlacementSeed = (int)entity;
- var resCache = IoCManager.Resolve();
// Initialize the background:
PaperBackground.ModulateSelfOverride = visuals.BackgroundModulate;
- var backgroundImage = visuals.BackgroundImagePath != null? resCache.GetResource(visuals.BackgroundImagePath) : null;
+ var backgroundImage = visuals.BackgroundImagePath != null? _resCache.GetResource(visuals.BackgroundImagePath) : null;
if (backgroundImage != null)
{
var backgroundImageMode = visuals.BackgroundImageTile ? StyleBoxTexture.StretchMode.Tile : StyleBoxTexture.StretchMode.Stretch;
@@ -127,7 +127,7 @@ public void InitVisuals(EntityUid entity, PaperVisualsComponent visuals)
PaperContent.ModulateSelfOverride = visuals.ContentImageModulate;
WrittenTextLabel.ModulateSelfOverride = visuals.FontAccentColor;
- var contentImage = visuals.ContentImagePath != null ? resCache.GetResource(visuals.ContentImagePath) : null;
+ var contentImage = visuals.ContentImagePath != null ? _resCache.GetResource(visuals.ContentImagePath) : null;
if (contentImage != null)
{
// Setup the paper content texture, but keep a reference to it, as we can't set
@@ -215,9 +215,9 @@ protected override void Draw(DrawingHandleScreen handle)
/// Initialize the paper contents, i.e. the text typed by the
/// user and any stamps that have peen put on the page.
///
- public void Populate(SharedPaperComponent.PaperBoundUserInterfaceState state)
+ public void Populate(PaperComponent.PaperBoundUserInterfaceState state)
{
- bool isEditing = state.Mode == SharedPaperComponent.PaperAction.Write;
+ bool isEditing = state.Mode == PaperComponent.PaperAction.Write;
bool wasEditing = InputContainer.Visible;
InputContainer.Visible = isEditing;
EditButtons.Visible = isEditing;
diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs
index cde5ba9ef79..93306f2fd1f 100644
--- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs
+++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorBoundUserInterface.cs
@@ -1,5 +1,5 @@
using Content.Shared.Singularity.Components;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.ParticleAccelerator.UI
{
@@ -16,9 +16,12 @@ protected override void Open()
{
base.Open();
- _menu = new ParticleAcceleratorControlMenu(this);
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
+
+ _menu.OnOverallState += SendEnableMessage;
+ _menu.OnPowerState += SendPowerStateMessage;
+ _menu.OnScan += SendScanPartsMessage;
}
public void SendEnableMessage(bool enable)
@@ -40,13 +43,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
{
_menu?.DataUpdate((ParticleAcceleratorUIState) state);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- _menu?.Dispose();
- _menu = null;
- }
}
}
diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs
deleted file mode 100644
index c69e0271372..00000000000
--- a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.cs
+++ /dev/null
@@ -1,521 +0,0 @@
-using System.Numerics;
-using Content.Client.Resources;
-using Content.Client.Stylesheets;
-using Content.Client.UserInterface.Controls;
-using Content.Shared.Singularity.Components;
-using Robust.Client.Animations;
-using Robust.Client.Graphics;
-using Robust.Client.ResourceManagement;
-using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Shared.Noise;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
-using static Robust.Client.UserInterface.Controls.BoxContainer;
-
-namespace Content.Client.ParticleAccelerator.UI
-{
- public sealed class ParticleAcceleratorControlMenu : BaseWindow
- {
- private readonly ShaderInstance _greyScaleShader;
-
- private readonly ParticleAcceleratorBoundUserInterface _owner;
-
- private readonly Label _drawLabel;
- private readonly FastNoiseLite _drawNoiseGenerator;
- private readonly Button _onButton;
- private readonly Button _offButton;
- private readonly Button _scanButton;
- private readonly Label _statusLabel;
- private readonly SpinBox _stateSpinBox;
-
- private readonly BoxContainer _alarmControl;
- private readonly Animation _alarmControlAnimation;
-
- private readonly PASegmentControl _endCapTexture;
- private readonly PASegmentControl _fuelChamberTexture;
- private readonly PASegmentControl _controlBoxTexture;
- private readonly PASegmentControl _powerBoxTexture;
- private readonly PASegmentControl _emitterForeTexture;
- private readonly PASegmentControl _emitterPortTexture;
- private readonly PASegmentControl _emitterStarboardTexture;
-
- private float _time;
- private int _lastDraw;
- private int _lastReceive;
-
- private bool _blockSpinBox;
- private bool _assembled;
- private bool _shouldContinueAnimating;
- private int _maxStrength = 3;
-
- public ParticleAcceleratorControlMenu(ParticleAcceleratorBoundUserInterface owner)
- {
- SetSize = new Vector2(400, 320);
- _greyScaleShader = IoCManager.Resolve().Index("Greyscale").Instance();
-
- _owner = owner;
- _drawNoiseGenerator = new();
- _drawNoiseGenerator.SetFractalType(FastNoiseLite.FractalType.FBm);
- _drawNoiseGenerator.SetFrequency(0.5f);
-
- var resourceCache = IoCManager.Resolve();
- var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
- var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
-
- MouseFilter = MouseFilterMode.Stop;
-
- _alarmControlAnimation = new Animation
- {
- Length = TimeSpan.FromSeconds(1),
- AnimationTracks =
- {
- new AnimationTrackControlProperty
- {
- Property = nameof(Control.Visible),
- KeyFrames =
- {
- new AnimationTrackProperty.KeyFrame(true, 0),
- new AnimationTrackProperty.KeyFrame(false, 0.75f),
- }
- }
- }
- };
-
- var back = new StyleBoxTexture
- {
- Texture = panelTex,
- Modulate = Color.FromHex("#25252A"),
- };
- back.SetPatchMargin(StyleBox.Margin.All, 10);
-
- var back2 = new StyleBoxTexture(back)
- {
- Modulate = Color.FromHex("#202023")
- };
-
- AddChild(new PanelContainer
- {
- PanelOverride = back,
- MouseFilter = MouseFilterMode.Pass
- });
-
- _stateSpinBox = new SpinBox { Value = 0, IsValid = StrengthSpinBoxValid };
- _stateSpinBox.InitDefaultButtons();
- _stateSpinBox.ValueChanged += PowerStateChanged;
- _stateSpinBox.LineEditDisabled = true;
-
- _offButton = new Button
- {
- ToggleMode = false,
- Text = Loc.GetString("particle-accelerator-control-menu-off-button"),
- StyleClasses = { StyleBase.ButtonOpenRight },
- };
- _offButton.OnPressed += args => owner.SendEnableMessage(false);
-
- _onButton = new Button
- {
- ToggleMode = false,
- Text = Loc.GetString("particle-accelerator-control-menu-on-button"),
- StyleClasses = { StyleBase.ButtonOpenLeft },
- };
- _onButton.OnPressed += args => owner.SendEnableMessage(true);
-
- var closeButton = new TextureButton
- {
- StyleClasses = { "windowCloseButton" },
- HorizontalAlignment = HAlignment.Right,
- Margin = new Thickness(0, 0, 8, 0)
- };
- closeButton.OnPressed += args => Close();
-
- var serviceManual = new Label
- {
- HorizontalAlignment = HAlignment.Center,
- StyleClasses = { StyleBase.StyleClassLabelSubText },
- Text = Loc.GetString("particle-accelerator-control-menu-service-manual-reference")
- };
- _drawLabel = new Label();
- var imgSize = new Vector2(32, 32);
- AddChild(new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- Children =
- {
- new Control
- {
- Margin = new Thickness(2, 2, 0, 0),
- Children =
- {
- new Label
- {
- Text = Loc.GetString("particle-accelerator-control-menu-device-version-label"),
- FontOverride = font,
- FontColorOverride = StyleNano.NanoGold,
- },
- closeButton
- }
- },
- new PanelContainer
- {
- PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold},
- MinSize = new Vector2(0, 2),
- },
- new Control
- {
- MinSize = new Vector2(0, 4)
- },
-
- new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- VerticalExpand = true,
- Children =
- {
- new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- Margin = new Thickness(4, 0, 0, 0),
- HorizontalExpand = true,
- Children =
- {
- new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- new Label
- {
- Text = Loc.GetString("particle-accelerator-control-menu-power-label") + " ",
- HorizontalExpand = true,
- HorizontalAlignment = HAlignment.Left,
- },
- _offButton,
- _onButton
- }
- },
- new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- new Label
- {
- Text = Loc.GetString("particle-accelerator-control-menu-strength-label") + " ",
- HorizontalExpand = true,
- HorizontalAlignment = HAlignment.Left,
- },
- _stateSpinBox
- }
- },
- new Control
- {
- MinSize = new Vector2(0, 10),
- },
- _drawLabel,
- new Control
- {
- VerticalExpand = true,
- },
- (_alarmControl = new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- Children =
- {
- new Label
- {
- Text = Loc.GetString("particle-accelerator-control-menu-alarm-control"),
- FontColorOverride = Color.Red,
- Align = Label.AlignMode.Center
- },
- serviceManual
- },
- Visible = false,
- }),
- }
- },
- new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- MinSize = new Vector2(186, 0),
- Children =
- {
- (_statusLabel = new Label
- {
- HorizontalAlignment = HAlignment.Center
- }),
- new Control
- {
- MinSize = new Vector2(0, 20)
- },
- new PanelContainer
- {
- HorizontalAlignment = HAlignment.Center,
- PanelOverride = back2,
- Children =
- {
- new GridContainer
- {
- Columns = 3,
- VSeparationOverride = 0,
- HSeparationOverride = 0,
- Children =
- {
- new Control {MinSize = imgSize},
- (_endCapTexture = Segment("end_cap")),
- new Control {MinSize = imgSize},
- (_controlBoxTexture = Segment("control_box")),
- (_fuelChamberTexture = Segment("fuel_chamber")),
- new Control {MinSize = imgSize},
- new Control {MinSize = imgSize},
- (_powerBoxTexture = Segment("power_box")),
- new Control {MinSize = imgSize},
- (_emitterStarboardTexture = Segment("emitter_starboard")),
- (_emitterForeTexture = Segment("emitter_fore")),
- (_emitterPortTexture = Segment("emitter_port")),
- }
- }
- }
- },
- (_scanButton = new Button
- {
- Text = Loc.GetString("particle-accelerator-control-menu-scan-parts-button"),
- HorizontalAlignment = HAlignment.Center
- })
- }
- }
- }
- },
- new StripeBack
- {
- Children =
- {
- new Label
- {
- Margin = new Thickness(4, 4, 0, 4),
- Text = Loc.GetString("particle-accelerator-control-menu-check-containment-field-warning"),
- HorizontalAlignment = HAlignment.Center,
- StyleClasses = {StyleBase.StyleClassLabelSubText},
- }
- }
- },
- new BoxContainer
- {
- Orientation = LayoutOrientation.Horizontal,
- Margin = new Thickness(12, 0, 0, 0),
- Children =
- {
- new Label
- {
- Text = Loc.GetString("particle-accelerator-control-menu-foo-bar-baz"),
- StyleClasses = {StyleBase.StyleClassLabelSubText}
- }
- }
- },
- }
- });
-
- _scanButton.OnPressed += args => _owner.SendScanPartsMessage();
-
- _alarmControl.AnimationCompleted += s =>
- {
- if (_shouldContinueAnimating)
- {
- _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim");
- }
- else
- {
- _alarmControl.Visible = false;
- }
- };
-
- PASegmentControl Segment(string name)
- {
- return new(this, resourceCache, name);
- }
-
- UpdateUI(false, false, false, false);
- }
-
- private bool StrengthSpinBoxValid(int n)
- {
- return n >= 0 && n <= _maxStrength && !_blockSpinBox;
- }
-
- private void PowerStateChanged(ValueChangedEventArgs e)
- {
- ParticleAcceleratorPowerState newState;
- switch (e.Value)
- {
- case 0:
- newState = ParticleAcceleratorPowerState.Standby;
- break;
- case 1:
- newState = ParticleAcceleratorPowerState.Level0;
- break;
- case 2:
- newState = ParticleAcceleratorPowerState.Level1;
- break;
- case 3:
- newState = ParticleAcceleratorPowerState.Level2;
- break;
- case 4:
- newState = ParticleAcceleratorPowerState.Level3;
- break;
- default:
- return;
- }
-
- _stateSpinBox.SetButtonDisabled(true);
- _owner.SendPowerStateMessage(newState);
- }
-
- protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
- {
- return DragMode.Move;
- }
-
- public void DataUpdate(ParticleAcceleratorUIState uiState)
- {
- _assembled = uiState.Assembled;
- UpdateUI(uiState.Assembled, uiState.InterfaceBlock, uiState.Enabled,
- uiState.WirePowerBlock);
- _statusLabel.Text = Loc.GetString("particle-accelerator-control-menu-status-label",
- ("status", Loc.GetString(uiState.Assembled ? "particle-accelerator-control-menu-status-operational" :
- "particle-accelerator-control-menu-status-incomplete")));
- UpdatePowerState(uiState.State, uiState.Enabled, uiState.Assembled,
- uiState.MaxLevel);
- UpdatePreview(uiState);
- _lastDraw = uiState.PowerDraw;
- _lastReceive = uiState.PowerReceive;
- }
-
- private void UpdatePowerState(ParticleAcceleratorPowerState state, bool enabled, bool assembled,
- ParticleAcceleratorPowerState maxState)
- {
- _stateSpinBox.OverrideValue(state switch
- {
- ParticleAcceleratorPowerState.Standby => 0,
- ParticleAcceleratorPowerState.Level0 => 1,
- ParticleAcceleratorPowerState.Level1 => 2,
- ParticleAcceleratorPowerState.Level2 => 3,
- ParticleAcceleratorPowerState.Level3 => 4,
- _ => 0
- });
-
-
- _maxStrength = maxState == ParticleAcceleratorPowerState.Level3 ? 4 : 3;
- if (_maxStrength > 3 && enabled && assembled)
- {
- _shouldContinueAnimating = true;
- if (!_alarmControl.HasRunningAnimation("warningAnim"))
- _alarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim");
- }
- else
- _shouldContinueAnimating = false;
- }
-
- private void UpdateUI(bool assembled, bool blocked, bool enabled, bool powerBlock)
- {
- _onButton.Pressed = enabled;
- _offButton.Pressed = !enabled;
-
- var cantUse = !assembled || blocked || powerBlock;
- _onButton.Disabled = cantUse;
- _offButton.Disabled = cantUse;
- _scanButton.Disabled = blocked;
-
- var cantChangeLevel = !assembled || blocked;
- _stateSpinBox.SetButtonDisabled(cantChangeLevel);
- _blockSpinBox = cantChangeLevel;
- }
-
- private void UpdatePreview(ParticleAcceleratorUIState updateMessage)
- {
- _endCapTexture.SetPowerState(updateMessage, updateMessage.EndCapExists);
- _controlBoxTexture.SetPowerState(updateMessage, true);
- _fuelChamberTexture.SetPowerState(updateMessage, updateMessage.FuelChamberExists);
- _powerBoxTexture.SetPowerState(updateMessage, updateMessage.PowerBoxExists);
- _emitterStarboardTexture.SetPowerState(updateMessage, updateMessage.EmitterStarboardExists);
- _emitterForeTexture.SetPowerState(updateMessage, updateMessage.EmitterForeExists);
- _emitterPortTexture.SetPowerState(updateMessage, updateMessage.EmitterPortExists);
- }
-
- protected override void FrameUpdate(FrameEventArgs args)
- {
- base.FrameUpdate(args);
-
- if (!_assembled)
- {
- _drawLabel.Text = Loc.GetString("particle-accelerator-control-menu-draw-not-available");
- return;
- }
-
- _time += args.DeltaSeconds;
-
- var watts = 0;
- if (_lastDraw != 0)
- {
- var val = _drawNoiseGenerator.GetNoise(_time, 0f);
- watts = (int) (_lastDraw + val * 5);
- }
-
- _drawLabel.Text = Loc.GetString("particle-accelerator-control-menu-draw",
- ("watts", $"{watts:##,##0}"),
- ("lastReceive", $"{_lastReceive:##,##0}"));
- }
-
- private sealed class PASegmentControl : Control
- {
- private readonly ParticleAcceleratorControlMenu _menu;
- private readonly string _baseState;
- private readonly TextureRect _base;
- private readonly TextureRect _unlit;
- private readonly RSI _rsi;
-
- public PASegmentControl(ParticleAcceleratorControlMenu menu, IResourceCache cache, string name)
- {
- _menu = menu;
- _baseState = name;
- _rsi = cache.GetResource($"/Textures/Structures/Power/Generation/PA/{name}.rsi").RSI;
-
- AddChild(_base = new TextureRect { Texture = _rsi[$"completed"].Frame0 });
- AddChild(_unlit = new TextureRect());
- MinSize = _rsi.Size;
- }
-
- public void SetPowerState(ParticleAcceleratorUIState state, bool exists)
- {
- _base.ShaderOverride = exists ? null : _menu._greyScaleShader;
- _base.ModulateSelfOverride = exists ? null : new Color(127, 127, 127);
-
- if (!state.Enabled || !exists)
- {
- _unlit.Visible = false;
- return;
- }
-
- _unlit.Visible = true;
-
- var suffix = state.State switch
- {
- ParticleAcceleratorPowerState.Standby => "_unlitp",
- ParticleAcceleratorPowerState.Level0 => "_unlitp0",
- ParticleAcceleratorPowerState.Level1 => "_unlitp1",
- ParticleAcceleratorPowerState.Level2 => "_unlitp2",
- ParticleAcceleratorPowerState.Level3 => "_unlitp3",
- _ => ""
- };
-
- if (!_rsi.TryGetState(_baseState + suffix, out var rState))
- {
- _unlit.Visible = false;
- return;
- }
-
- _unlit.Texture = rState.Frame0;
- }
- }
- }
-}
diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml
new file mode 100644
index 00000000000..63f15837068
--- /dev/null
+++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml.cs b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml.cs
new file mode 100644
index 00000000000..8b21e7d94bd
--- /dev/null
+++ b/Content.Client/ParticleAccelerator/UI/ParticleAcceleratorControlMenu.xaml.cs
@@ -0,0 +1,322 @@
+using System.Numerics;
+using Content.Client.Message;
+using Content.Client.Resources;
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Singularity.Components;
+using Content.Shared.Access.Systems;
+using Robust.Client.Animations;
+using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Noise;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+using Robust.Client.Player;
+
+namespace Content.Client.ParticleAccelerator.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class ParticleAcceleratorControlMenu : FancyWindow
+{
+ [Dependency] private readonly IResourceCache _cache = default!;
+
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPlayerManager _player = default!;
+
+ private readonly AccessReaderSystem _accessReader;
+
+ private readonly FastNoiseLite _drawNoiseGenerator;
+
+ private readonly Animation _alarmControlAnimation;
+
+ private float _time;
+ private int _lastDraw;
+ private int _lastReceive;
+
+ private bool _assembled;
+ private bool _shouldContinueAnimating;
+ private int _maxStrength = 3;
+
+ public event Action? OnOverallState;
+ public event Action? OnScan;
+ public event Action? OnPowerState;
+
+ private EntityUid _entity;
+
+ public ParticleAcceleratorControlMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ _accessReader = _entityManager.System();
+ _drawNoiseGenerator = new();
+ _drawNoiseGenerator.SetFractalType(FastNoiseLite.FractalType.FBm);
+ _drawNoiseGenerator.SetFrequency(0.5f);
+
+ var panelTex = _cache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
+
+ MouseFilter = MouseFilterMode.Stop;
+
+ _alarmControlAnimation = new Animation
+ {
+ Length = TimeSpan.FromSeconds(1),
+ AnimationTracks =
+ {
+ new AnimationTrackControlProperty
+ {
+ Property = nameof(Visible),
+ KeyFrames =
+ {
+ new AnimationTrackProperty.KeyFrame(true, 0),
+ new AnimationTrackProperty.KeyFrame(false, 0.75f),
+ }
+ }
+ }
+ };
+
+ if (BackPanel.PanelOverride is StyleBoxTexture tex)
+ tex.Texture = panelTex;
+
+ StatusLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-status-label"));
+ StatusStateLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-status-unknown"));
+ PowerLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-power-label"));
+ StrengthLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-strength-label"));
+ BigAlarmLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-alarm-control-1"));
+ BigAlarmLabelTwo.SetMarkup(Loc.GetString("particle-accelerator-control-menu-alarm-control-2"));
+ DrawLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-draw"));
+
+ StateSpinBox.IsValid = StrengthSpinBoxValid;
+ StateSpinBox.InitDefaultButtons();
+ StateSpinBox.ValueChanged += PowerStateChanged;
+ StateSpinBox.LineEditDisabled = true;
+
+ OffButton.OnPressed += _ =>
+ {
+ OnOverallState?.Invoke(false);
+ };
+
+ OnButton.OnPressed += _ =>
+ {
+ OnOverallState?.Invoke(true);
+ };
+
+ ScanButton.OnPressed += _ =>
+ {
+ OnScan?.Invoke();
+ };
+
+ AlarmControl.AnimationCompleted += _ =>
+ {
+ if (_shouldContinueAnimating)
+ {
+ AlarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim");
+ }
+ else
+ {
+ AlarmControl.Visible = false;
+ }
+ };
+
+ UpdateUI(false, false, false, false);
+ }
+
+ public void SetEntity(EntityUid uid)
+ {
+ _entity = uid;
+ }
+
+ private void PowerStateChanged(ValueChangedEventArgs e)
+ {
+ ParticleAcceleratorPowerState newState;
+ switch (e.Value)
+ {
+ case 0:
+ newState = ParticleAcceleratorPowerState.Standby;
+ break;
+ case 1:
+ newState = ParticleAcceleratorPowerState.Level0;
+ break;
+ case 2:
+ newState = ParticleAcceleratorPowerState.Level1;
+ break;
+ case 3:
+ newState = ParticleAcceleratorPowerState.Level2;
+ break;
+ case 4:
+ newState = ParticleAcceleratorPowerState.Level3;
+ break;
+ default:
+ return;
+ }
+
+ StateSpinBox.SetButtonDisabled(true);
+ OnPowerState?.Invoke(newState);
+ }
+
+ private bool StrengthSpinBoxValid(int n)
+ {
+ return n >= 0 && n <= _maxStrength;
+ }
+
+ protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
+ {
+ return DragMode.Move;
+ }
+
+ public void DataUpdate(ParticleAcceleratorUIState uiState)
+ {
+ _assembled = uiState.Assembled;
+ UpdateUI(uiState.Assembled,
+ uiState.InterfaceBlock,
+ uiState.Enabled,
+ uiState.WirePowerBlock);
+ StatusStateLabel.SetMarkup(Loc.GetString(uiState.Assembled
+ ? "particle-accelerator-control-menu-status-operational"
+ : "particle-accelerator-control-menu-status-incomplete"));
+ UpdatePowerState(uiState.State, uiState.Enabled, uiState.Assembled, uiState.MaxLevel);
+ UpdatePreview(uiState);
+ _lastDraw = uiState.PowerDraw;
+ _lastReceive = uiState.PowerReceive;
+ }
+
+ private void UpdatePowerState(ParticleAcceleratorPowerState state, bool enabled, bool assembled, ParticleAcceleratorPowerState maxState)
+ {
+ var value = state switch
+ {
+ ParticleAcceleratorPowerState.Standby => 0,
+ ParticleAcceleratorPowerState.Level0 => 1,
+ ParticleAcceleratorPowerState.Level1 => 2,
+ ParticleAcceleratorPowerState.Level2 => 3,
+ ParticleAcceleratorPowerState.Level3 => 4,
+ _ => 0
+ };
+
+ StateSpinBox.OverrideValue(value);
+
+ _maxStrength = maxState == ParticleAcceleratorPowerState.Level3 ? 4 : 3;
+ if (_maxStrength > 3 && enabled && assembled)
+ {
+ _shouldContinueAnimating = true;
+ if (!AlarmControl.HasRunningAnimation("warningAnim"))
+ AlarmControl.PlayAnimation(_alarmControlAnimation, "warningAnim");
+ }
+ else
+ _shouldContinueAnimating = false;
+ }
+
+ private void UpdateUI(bool assembled, bool blocked, bool enabled, bool powerBlock)
+ {
+ bool hasAccess = _player.LocalSession?.AttachedEntity is {} player
+ && _accessReader.IsAllowed(player, _entity);
+
+ OnButton.Pressed = enabled;
+ OffButton.Pressed = !enabled;
+
+ var cantUse = !assembled || blocked || powerBlock || !hasAccess;
+ OnButton.Disabled = cantUse;
+ OffButton.Disabled = cantUse;
+ ScanButton.Disabled = blocked || !hasAccess;
+
+ var cantChangeLevel = !assembled || blocked || !enabled || cantUse;
+ StateSpinBox.SetButtonDisabled(cantChangeLevel);
+ }
+
+ private void UpdatePreview(ParticleAcceleratorUIState updateMessage)
+ {
+ EndCapTexture.SetPowerState(updateMessage, updateMessage.EndCapExists);
+ ControlBoxTexture.SetPowerState(updateMessage, true);
+ FuelChamberTexture.SetPowerState(updateMessage, updateMessage.FuelChamberExists);
+ PowerBoxTexture.SetPowerState(updateMessage, updateMessage.PowerBoxExists);
+ EmitterStarboardTexture.SetPowerState(updateMessage, updateMessage.EmitterStarboardExists);
+ EmitterForeTexture.SetPowerState(updateMessage, updateMessage.EmitterForeExists);
+ EmitterPortTexture.SetPowerState(updateMessage, updateMessage.EmitterPortExists);
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+
+ if (!_assembled)
+ {
+ DrawValueLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-draw-not-available"));
+ return;
+ }
+
+ _time += args.DeltaSeconds;
+
+ var watts = 0;
+ if (_lastDraw != 0)
+ {
+ var val = _drawNoiseGenerator.GetNoise(_time, 0f);
+ watts = (int) (_lastDraw + val * 5);
+ }
+
+ DrawValueLabel.SetMarkup(Loc.GetString("particle-accelerator-control-menu-draw-value",
+ ("watts", $"{watts:##,##0}"),
+ ("lastReceive", $"{_lastReceive:##,##0}")));
+ }
+}
+
+public sealed class PASegmentControl : Control
+{
+ private readonly ShaderInstance _greyScaleShader;
+ private readonly TextureRect _base;
+ private readonly TextureRect _unlit;
+ private RSI? _rsi;
+
+ public string BaseState { get; set; } = "control_box";
+
+ public PASegmentControl()
+ {
+ _greyScaleShader = IoCManager.Resolve().Index("Greyscale").Instance();
+
+ AddChild(_base = new TextureRect());
+ AddChild(_unlit = new TextureRect());
+ }
+
+ protected override void EnteredTree()
+ {
+ base.EnteredTree();
+ _rsi = IoCManager.Resolve().GetResource($"/Textures/Structures/Power/Generation/PA/{BaseState}.rsi").RSI;
+ MinSize = _rsi.Size;
+ _base.Texture = _rsi["completed"].Frame0;
+ }
+
+ public void SetPowerState(ParticleAcceleratorUIState state, bool exists)
+ {
+ _base.ShaderOverride = exists ? null : _greyScaleShader;
+ _base.ModulateSelfOverride = exists ? null : new Color(127, 127, 127);
+
+ if (!state.Enabled || !exists)
+ {
+ _unlit.Visible = false;
+ return;
+ }
+
+ _unlit.Visible = true;
+
+ var suffix = state.State switch
+ {
+ ParticleAcceleratorPowerState.Standby => "_unlitp",
+ ParticleAcceleratorPowerState.Level0 => "_unlitp0",
+ ParticleAcceleratorPowerState.Level1 => "_unlitp1",
+ ParticleAcceleratorPowerState.Level2 => "_unlitp2",
+ ParticleAcceleratorPowerState.Level3 => "_unlitp3",
+ _ => ""
+ };
+
+ if (_rsi == null)
+ return;
+
+ if (!_rsi.TryGetState(BaseState + suffix, out var rState))
+ {
+ _unlit.Visible = false;
+ return;
+ }
+
+ _unlit.Texture = rState.Frame0;
+ }
+}
diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs
index 31042854d4a..74a5e7afdcd 100644
--- a/Content.Client/Physics/Controllers/MoverController.cs
+++ b/Content.Client/Physics/Controllers/MoverController.cs
@@ -28,57 +28,57 @@ public override void Initialize()
SubscribeLocalEvent(OnUpdatePullablePredicted);
}
- private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args)
+ private void OnUpdatePredicted(Entity entity, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is controlled by the player
- if (uid == _playerManager.LocalEntity)
+ if (entity.Owner == _playerManager.LocalEntity)
args.IsPredicted = true;
}
- private void OnUpdateRelayTargetPredicted(EntityUid uid, MovementRelayTargetComponent component, ref UpdateIsPredictedEvent args)
+ private void OnUpdateRelayTargetPredicted(Entity entity, ref UpdateIsPredictedEvent args)
{
- if (component.Source == _playerManager.LocalEntity)
+ if (entity.Comp.Source == _playerManager.LocalEntity)
args.IsPredicted = true;
}
- private void OnUpdatePullablePredicted(EntityUid uid, PullableComponent component, ref UpdateIsPredictedEvent args)
+ private void OnUpdatePullablePredicted(Entity entity, 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.
- if (component.Puller == _playerManager.LocalEntity)
+ if (entity.Comp.Puller == _playerManager.LocalEntity)
args.IsPredicted = true;
- else if (component.Puller != null)
+ else if (entity.Comp.Puller != null)
args.BlockPrediction = true;
// TODO recursive pulling checks?
// What if the entity is being pulled by a vehicle controlled by the player?
}
- private void OnRelayPlayerAttached(EntityUid uid, RelayInputMoverComponent component, LocalPlayerAttachedEvent args)
+ private void OnRelayPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args)
{
- Physics.UpdateIsPredicted(uid);
- Physics.UpdateIsPredicted(component.RelayEntity);
- if (MoverQuery.TryGetComponent(component.RelayEntity, out var inputMover))
- SetMoveInput(inputMover, MoveButtons.None);
+ Physics.UpdateIsPredicted(entity.Owner);
+ Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Owner, inputMover), MoveButtons.None);
}
- private void OnRelayPlayerDetached(EntityUid uid, RelayInputMoverComponent component, LocalPlayerDetachedEvent args)
+ private void OnRelayPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args)
{
- Physics.UpdateIsPredicted(uid);
- Physics.UpdateIsPredicted(component.RelayEntity);
- if (MoverQuery.TryGetComponent(component.RelayEntity, out var inputMover))
- SetMoveInput(inputMover, MoveButtons.None);
+ Physics.UpdateIsPredicted(entity.Owner);
+ Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Owner, inputMover), MoveButtons.None);
}
- private void OnPlayerAttached(EntityUid uid, InputMoverComponent component, LocalPlayerAttachedEvent args)
+ private void OnPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args)
{
- SetMoveInput(component, MoveButtons.None);
+ SetMoveInput(entity, MoveButtons.None);
}
- private void OnPlayerDetached(EntityUid uid, InputMoverComponent component, LocalPlayerDetachedEvent args)
+ private void OnPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args)
{
- SetMoveInput(component, MoveButtons.None);
+ SetMoveInput(entity, MoveButtons.None);
}
public override void UpdateBeforeSolve(bool prediction, float frameTime)
diff --git a/Content.Client/Physics/JointVisualsOverlay.cs b/Content.Client/Physics/JointVisualsOverlay.cs
index 09c02746e2e..e0b3499a974 100644
--- a/Content.Client/Physics/JointVisualsOverlay.cs
+++ b/Content.Client/Physics/JointVisualsOverlay.cs
@@ -58,8 +58,8 @@ protected override void Draw(in OverlayDrawArgs args)
coordsA = coordsA.Offset(rotA.RotateVec(visuals.OffsetA));
coordsB = coordsB.Offset(rotB.RotateVec(visuals.OffsetB));
- var posA = coordsA.ToMapPos(_entManager, xformSystem);
- var posB = coordsB.ToMapPos(_entManager, xformSystem);
+ var posA = xformSystem.ToMapCoordinates(coordsA).Position;
+ var posB = xformSystem.ToMapCoordinates(coordsB).Position;
var diff = (posB - posA);
var length = diff.Length();
diff --git a/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs b/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs
index 3ebcf7cbced..0df6787170a 100644
--- a/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs
+++ b/Content.Client/Pinpointer/UI/NavMapBeaconBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Pinpointer;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Pinpointer.UI;
@@ -16,19 +17,16 @@ public NavMapBeaconBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner,
protected override void Open()
{
base.Open();
- _window = new NavMapBeaconWindow(Owner);
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
+
+ if (EntMan.TryGetComponent(Owner, out NavMapBeaconComponent? beacon))
+ {
+ _window.SetEntity(Owner, beacon);
+ }
_window.OnApplyButtonPressed += (label, enabled, color) =>
{
SendMessage(new NavMapBeaconConfigureBuiMessage(label, enabled, color));
};
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- _window?.Dispose();
- }
}
diff --git a/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs b/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs
index 968fe188f75..b77f1af0472 100644
--- a/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs
+++ b/Content.Client/Pinpointer/UI/NavMapBeaconWindow.xaml.cs
@@ -10,38 +10,37 @@ namespace Content.Client.Pinpointer.UI;
[GenerateTypedNameReferences]
public sealed partial class NavMapBeaconWindow : FancyWindow
{
- [Dependency] private readonly IEntityManager _entityManager = default!;
-
private string? _defaultLabel;
private bool _defaultEnabled;
private Color _defaultColor;
public event Action? OnApplyButtonPressed;
- public NavMapBeaconWindow(EntityUid beaconEntity)
+ public NavMapBeaconWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- if (!_entityManager.TryGetComponent(beaconEntity, out var navMap))
- return;
- _defaultLabel = navMap.Text;
- _defaultEnabled = navMap.Enabled;
- _defaultColor = navMap.Color;
- UpdateVisibleButton(navMap.Enabled);
VisibleButton.OnPressed += args => UpdateVisibleButton(args.Button.Pressed);
-
- LabelLineEdit.Text = navMap.Text ?? string.Empty;
LabelLineEdit.OnTextChanged += OnTextChanged;
-
- ColorSelector.Color = navMap.Color;
ColorSelector.OnColorChanged += _ => TryEnableApplyButton();
TryEnableApplyButton();
ApplyButton.OnPressed += OnApplyPressed;
}
+ public void SetEntity(EntityUid uid, NavMapBeaconComponent navMap)
+ {
+ _defaultLabel = navMap.Text;
+ _defaultEnabled = navMap.Enabled;
+ _defaultColor = navMap.Color;
+
+ UpdateVisibleButton(navMap.Enabled);
+ LabelLineEdit.Text = navMap.Text ?? string.Empty;
+ ColorSelector.Color = navMap.Color;
+ }
+
private void UpdateVisibleButton(bool value)
{
VisibleButton.Pressed = value;
diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs
index 3c99a188181..413b41c36a6 100644
--- a/Content.Client/Pinpointer/UI/NavMapControl.cs
+++ b/Content.Client/Pinpointer/UI/NavMapControl.cs
@@ -228,8 +228,8 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
if (!blip.Selectable)
continue;
-
- var currentDistance = (blip.Coordinates.ToMapPos(EntManager, _transformSystem) - worldPosition).Length();
+
+ var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length();
if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance)
continue;
@@ -397,7 +397,7 @@ protected override void Draw(DrawingHandleScreen handle)
{
if (lit && value.Visible)
{
- var mapPos = coord.ToMap(EntManager, _transformSystem);
+ var mapPos = _transformSystem.ToMapCoordinates(coord);
if (mapPos.MapId != MapId.Nullspace)
{
@@ -418,7 +418,7 @@ protected override void Draw(DrawingHandleScreen handle)
if (blip.Texture == null)
continue;
- var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem);
+ var mapPos = _transformSystem.ToMapCoordinates(blip.Coordinates);
if (mapPos.MapId != MapId.Nullspace)
{
@@ -535,7 +535,7 @@ private void UpdateNavMapWallLines()
// East edge
neighborData = 0;
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
- neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
+ neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
@@ -548,7 +548,7 @@ private void UpdateNavMapWallLines()
// South edge
neighborData = 0;
if (relativeTile.Y != 0)
- neighborData = chunk.TileData[i-1];
+ neighborData = chunk.TileData[i - 1];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
@@ -561,7 +561,7 @@ private void UpdateNavMapWallLines()
// West edge
neighborData = 0;
if (relativeTile.X != 0)
- neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
+ neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
diff --git a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs
index 1483e75e732..91fb4ef71bd 100644
--- a/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs
+++ b/Content.Client/Pinpointer/UI/StationMapBoundUserInterface.cs
@@ -1,4 +1,5 @@
-using Robust.Client.GameObjects;
+using Content.Shared.Pinpointer;
+using Robust.Client.UserInterface;
namespace Content.Client.Pinpointer.UI;
@@ -14,7 +15,6 @@ public StationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, u
protected override void Open()
{
base.Open();
- _window?.Close();
EntityUid? gridUid = null;
if (EntMan.TryGetComponent(Owner, out var xform))
@@ -22,14 +22,11 @@ protected override void Open()
gridUid = xform.GridUid;
}
- _window = new StationMapWindow(gridUid, Owner);
- _window.OpenCentered();
- _window.OnClose += Close;
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- _window?.Dispose();
+ _window = this.CreateWindow();
+ _window.Title = EntMan.GetComponent(Owner).EntityName;
+ if (EntMan.TryGetComponent(Owner, out var comp) && comp.ShowLocation)
+ _window.Set(gridUid, Owner);
+ else
+ _window.Set(gridUid, null);
}
}
diff --git a/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs b/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs
index 1b01fe4e304..7cbb8b7d0db 100644
--- a/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs
+++ b/Content.Client/Pinpointer/UI/StationMapWindow.xaml.cs
@@ -9,19 +9,18 @@ namespace Content.Client.Pinpointer.UI;
[GenerateTypedNameReferences]
public sealed partial class StationMapWindow : FancyWindow
{
- public StationMapWindow(EntityUid? mapUid, EntityUid? trackedEntity)
+ public StationMapWindow()
{
RobustXamlLoader.Load(this);
+ }
+
+ public void Set(EntityUid? mapUid, EntityUid? trackedEntity)
+ {
NavMapScreen.MapUid = mapUid;
if (trackedEntity != null)
NavMapScreen.TrackedCoordinates.Add(new EntityCoordinates(trackedEntity.Value, Vector2.Zero), (true, Color.Cyan));
- if (IoCManager.Resolve().TryGetComponent(mapUid, out var metadata))
- {
- Title = metadata.EntityName;
- }
-
NavMapScreen.ForceNavMapUpdate();
}
}
diff --git a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs b/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs
deleted file mode 100644
index 57965b030a2..00000000000
--- a/Content.Client/Pinpointer/UI/UntrackedMapBoundUserInterface.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Robust.Client.GameObjects;
-
-namespace Content.Client.Pinpointer.UI;
-
-public sealed class UntrackedStationMapBoundUserInterface : BoundUserInterface
-{
- [ViewVariables]
- private StationMapWindow? _window;
-
- public UntrackedStationMapBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
- {
- }
-
- protected override void Open()
- {
- base.Open();
- _window?.Close();
- EntityUid? gridUid = null;
-
- if (EntMan.TryGetComponent(Owner, out var xform))
- {
- gridUid = xform.GridUid;
- }
-
- _window = new StationMapWindow(gridUid, null);
- _window.OpenCentered();
- _window.OnClose += Close;
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- _window?.Dispose();
- }
-}
diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
index 87eb5234013..1ecf9e339ab 100644
--- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
+++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs
@@ -1,8 +1,10 @@
using System.Diagnostics.CodeAnalysis;
+using Content.Client.Lobby;
using Content.Shared.CCVar;
using Content.Shared.Players;
using Content.Shared.Players.JobWhitelist;
using Content.Shared.Players.PlayTimeTracking;
+using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Client;
using Robust.Client.Player;
@@ -90,7 +92,7 @@ private void RxJobWhitelist(MsgJobWhitelist message)
Updated?.Invoke();
}
- public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
+ public bool IsAllowed(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
{
reason = null;
@@ -107,16 +109,16 @@ public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessag
if (player == null)
return true;
- return CheckRoleTime(job, out reason);
+ return CheckRoleRequirements(job, profile, out reason);
}
- public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
+ public bool CheckRoleRequirements(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
{
var reqs = _entManager.System().GetJobRequirement(job);
- return CheckRoleTime(reqs, out reason);
+ return CheckRoleRequirements(reqs, profile, out reason);
}
- public bool CheckRoleTime(HashSet? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
+ public bool CheckRoleRequirements(HashSet? requirements, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
{
reason = null;
@@ -126,7 +128,7 @@ public bool CheckRoleTime(HashSet? requirements, [NotNullWhen(fa
var reasons = new List();
foreach (var requirement in requirements)
{
- if (JobRequirements.TryRequirementMet(requirement, _roles, out var jobReason, _entManager, _prototypes, _whitelisted))
+ if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason, _whitelisted))
continue;
reasons.Add(jobReason.ToMarkup());
diff --git a/Content.Client/Power/APC/ApcBoundUserInterface.cs b/Content.Client/Power/APC/ApcBoundUserInterface.cs
index fbcbf011569..759a5949ba6 100644
--- a/Content.Client/Power/APC/ApcBoundUserInterface.cs
+++ b/Content.Client/Power/APC/ApcBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.APC;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Power.APC
{
@@ -19,9 +20,8 @@ protected override void Open()
{
base.Open();
- _menu = new ApcMenu(this);
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnBreaker += BreakerPressed;
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -36,15 +36,5 @@ public void BreakerPressed()
{
SendMessage(new ApcToggleMainBreakerMessage());
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _menu?.Dispose();
- }
- }
}
}
diff --git a/Content.Client/Power/APC/UI/ApcMenu.xaml.cs b/Content.Client/Power/APC/UI/ApcMenu.xaml.cs
index dbf68ea07b0..2f61ea63a86 100644
--- a/Content.Client/Power/APC/UI/ApcMenu.xaml.cs
+++ b/Content.Client/Power/APC/UI/ApcMenu.xaml.cs
@@ -17,13 +17,19 @@ namespace Content.Client.Power.APC.UI
[GenerateTypedNameReferences]
public sealed partial class ApcMenu : FancyWindow
{
- public ApcMenu(ApcBoundUserInterface owner)
+ public event Action? OnBreaker;
+
+ public ApcMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
- EntityView.SetEntity(owner.Owner);
- BreakerButton.OnPressed += _ => owner.BreakerPressed();
+ BreakerButton.OnPressed += _ => OnBreaker?.Invoke();
+ }
+
+ public void SetEntity(EntityUid entity)
+ {
+ EntityView.SetEntity(entity);
}
public void UpdateState(BoundUserInterfaceState state)
diff --git a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs
index 4d56592c232..61e20f751ca 100644
--- a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs
+++ b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs
@@ -1,6 +1,7 @@
-using Content.Client.Power.Components;
+using Content.Client.Power.Components;
using Content.Shared.Power.Components;
using Content.Shared.Power.EntitySystems;
+using Content.Shared.Examine;
using Robust.Shared.GameStates;
namespace Content.Client.Power.EntitySystems;
@@ -10,9 +11,15 @@ public sealed class PowerReceiverSystem : SharedPowerReceiverSystem
public override void Initialize()
{
base.Initialize();
+ SubscribeLocalEvent(OnExamined);
SubscribeLocalEvent(OnHandleState);
}
+ private void OnExamined(Entity ent, ref ExaminedEvent args)
+ {
+ args.PushMarkup(GetExamineText(ent.Comp.Powered));
+ }
+
private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentHandleState args)
{
if (args.Current is not ApcPowerReceiverComponentState state)
diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs
index bd5b75de1da..e975e5d466e 100644
--- a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs
+++ b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs
@@ -9,35 +9,39 @@ namespace Content.Client.Power.Generator;
[GenerateTypedNameReferences]
public sealed partial class GeneratorWindow : FancyWindow
{
- private readonly EntityUid _entity;
-
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
- private readonly SharedPowerSwitchableSystem _switchable;
- private readonly FuelGeneratorComponent? _component;
- private PortableGeneratorComponentBuiState? _lastState;
+ private EntityUid _entity;
+
+ public float? MaximumPower;
+
+ public event Action? OnPower;
+ public event Action? OnState;
+ public event Action? OnSwitchOutput;
+ public event Action? OnEjectFuel;
- public GeneratorWindow(PortableGeneratorBoundUserInterface bui, EntityUid entity)
+ public GeneratorWindow()
{
- _entity = entity;
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- _entityManager.TryGetComponent(entity, out _component);
- _switchable = _entityManager.System();
-
- EntityView.SetEntity(entity);
TargetPower.IsValid += IsValid;
TargetPower.ValueChanged += (args) =>
{
- bui.SetTargetPower(args.Value);
+ OnPower?.Invoke(args.Value);
};
- StartButton.OnPressed += _ => bui.Start();
- StopButton.OnPressed += _ => bui.Stop();
- OutputSwitchButton.OnPressed += _ => bui.SwitchOutput();
- FuelEject.OnPressed += _ => bui.EjectFuel();
+ StartButton.OnPressed += _ => OnState?.Invoke(true);
+ StopButton.OnPressed += _ => OnState?.Invoke(false);
+ OutputSwitchButton.OnPressed += _ => OnSwitchOutput?.Invoke();
+ FuelEject.OnPressed += _ => OnEjectFuel?.Invoke();
+ }
+
+ public void SetEntity(EntityUid entity)
+ {
+ _entity = entity;
+ EntityView.SetEntity(entity);
}
private bool IsValid(int arg)
@@ -45,7 +49,7 @@ private bool IsValid(int arg)
if (arg < 0)
return false;
- if (arg > (_lastState?.MaximumPower / 1000.0f ?? 0))
+ if (arg > (MaximumPower / 1000.0f ?? 0))
return false;
return true;
@@ -53,16 +57,17 @@ private bool IsValid(int arg)
public void Update(PortableGeneratorComponentBuiState state)
{
- if (_component == null)
+ MaximumPower = state.MaximumPower;
+
+ if (!_entityManager.TryGetComponent(_entity, out FuelGeneratorComponent? component))
return;
- _lastState = state;
if (!TargetPower.LineEditControl.HasKeyboardFocus())
TargetPower.OverrideValue((int)(state.TargetPower / 1000.0f));
- var efficiency = SharedGeneratorSystem.CalcFuelEfficiency(state.TargetPower, state.OptimalPower, _component);
+ var efficiency = SharedGeneratorSystem.CalcFuelEfficiency(state.TargetPower, state.OptimalPower, component);
Efficiency.Text = efficiency.ToString("P1");
- var burnRate = _component.OptimalBurnRate / efficiency;
+ var burnRate = component.OptimalBurnRate / efficiency;
var left = state.RemainingFuel / burnRate;
Eta.Text = Loc.GetString(
@@ -102,14 +107,15 @@ public void Update(PortableGeneratorComponentBuiState state)
}
var canSwitch = _entityManager.TryGetComponent(_entity, out PowerSwitchableComponent? switchable);
+ var switcher = _entityManager.System();
OutputSwitchLabel.Visible = canSwitch;
OutputSwitchButton.Visible = canSwitch;
if (switchable != null)
{
- var voltage = _switchable.VoltageString(_switchable.GetVoltage(_entity, switchable));
+ var voltage = switcher.VoltageString(switcher.GetVoltage(_entity, switchable));
OutputSwitchLabel.Text = Loc.GetString("portable-generator-ui-current-output", ("voltage", voltage));
- var nextVoltage = _switchable.VoltageString(_switchable.GetNextVoltage(_entity, switchable));
+ var nextVoltage = switcher.VoltageString(switcher.GetNextVoltage(_entity, switchable));
OutputSwitchButton.Text = Loc.GetString("power-switchable-switch-voltage", ("voltage", nextVoltage));
OutputSwitchButton.Disabled = state.On;
}
diff --git a/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs b/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs
index 30679d71fd6..550e1041b62 100644
--- a/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs
+++ b/Content.Client/Power/Generator/PortableGeneratorBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Power.Generator;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Power.Generator;
@@ -16,10 +17,25 @@ public PortableGeneratorBoundUserInterface(EntityUid owner, Enum uiKey) : base(o
protected override void Open()
{
base.Open();
- _window = new GeneratorWindow(this, Owner);
+ _window = this.CreateWindow();
+ _window.SetEntity(Owner);
+ _window.OnState += args =>
+ {
+ if (args)
+ {
+ Start();
+ }
+ else
+ {
+ Stop();
+ }
+ };
+
+ _window.OnPower += SetTargetPower;
+ _window.OnEjectFuel += EjectFuel;
+ _window.OnSwitchOutput += SwitchOutput;
_window.OpenCenteredLeft();
- _window.OnClose += Close;
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -30,11 +46,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.Update(msg);
}
- protected override void Dispose(bool disposing)
- {
- _window?.Dispose();
- }
-
public void SetTargetPower(int target)
{
SendMessage(new PortableGeneratorSetTargetPowerMessage(target));
diff --git a/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs b/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs
index dc1dcd03ef1..cbc343c06c6 100644
--- a/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs
+++ b/Content.Client/Power/PowerMonitoringConsoleBoundUserInterface.cs
@@ -1,4 +1,5 @@
using Content.Shared.Power;
+using Robust.Client.UserInterface;
namespace Content.Client.Power;
@@ -11,9 +12,9 @@ public PowerMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : b
protected override void Open()
{
- _menu = new PowerMonitoringWindow(this, Owner);
- _menu.OpenCentered();
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
+ _menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -22,9 +23,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
var castState = (PowerMonitoringConsoleBoundInterfaceState) state;
- if (castState == null)
- return;
-
EntMan.TryGetComponent(Owner, out var xform);
_menu?.ShowEntites
(castState.TotalSources,
@@ -40,13 +38,4 @@ public void SendPowerMonitoringConsoleMessage(NetEntity? netEntity, PowerMonitor
{
SendMessage(new PowerMonitoringConsoleMessage(netEntity, group));
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs
index 74752ddc534..d9952992070 100644
--- a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs
+++ b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs
@@ -32,7 +32,7 @@ private void UpdateWindowConsoleEntry
if (windowEntry == null)
return;
- // Update sources and loads
+ // Update sources and loads
UpdateEntrySourcesOrLoads(masterContainer, windowEntry.SourcesContainer, focusSources, _sourceIcon);
UpdateEntrySourcesOrLoads(masterContainer, windowEntry.LoadsContainer, focusLoads, _loadIconPath);
@@ -134,7 +134,7 @@ private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContaine
subEntry.Button.OnButtonUp += args => { ButtonAction(subEntry, masterContainer); };
}
- if (!_entManager.TryGetComponent(_owner, out var console))
+ if (!_entManager.TryGetComponent(Entity, out var console))
return;
// Update all children
@@ -379,7 +379,7 @@ public PowerMonitoringWindowEntry(PowerMonitoringConsoleEntry entry) : base(entr
AddChild(MainContainer);
- // Grid container to hold the list of sources when selected
+ // Grid container to hold the list of sources when selected
SourcesContainer = new BoxContainer()
{
Orientation = LayoutOrientation.Vertical,
diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.cs
index 81fe1f4d047..e3043252486 100644
--- a/Content.Client/Power/PowerMonitoringWindow.xaml.cs
+++ b/Content.Client/Power/PowerMonitoringWindow.xaml.cs
@@ -15,13 +15,12 @@ namespace Content.Client.Power;
[GenerateTypedNameReferences]
public sealed partial class PowerMonitoringWindow : FancyWindow
{
- private readonly IEntityManager _entManager;
+ [Dependency] private IEntityManager _entManager = default!;
private readonly SpriteSystem _spriteSystem;
- private readonly IGameTiming _gameTiming;
+ [Dependency] private IGameTiming _gameTiming = default!;
private const float BlinkFrequency = 1f;
- private EntityUid? _owner;
private NetEntity? _focusEntity;
public event Action? SendPowerMonitoringConsoleMessageAction;
@@ -34,31 +33,56 @@ public sealed partial class PowerMonitoringWindow : FancyWindow
{ PowerMonitoringConsoleGroup.APC, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), Color.LimeGreen) },
};
- public PowerMonitoringWindow(PowerMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
+ public EntityUid Entity;
+
+ public PowerMonitoringWindow()
{
RobustXamlLoader.Load(this);
- _entManager = IoCManager.Resolve();
- _gameTiming = IoCManager.Resolve();
+ IoCManager.InjectDependencies(this);
_spriteSystem = _entManager.System();
- _owner = owner;
+
+ // Set trackable entity selected action
+ NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
+
+ // Update nav map
+ NavMap.ForceNavMapUpdate();
+
+ // Set UI tab titles
+ MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources"));
+ MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes"));
+ MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation"));
+ MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc"));
+
+ // Track when the MasterTabContainer changes its tab
+ MasterTabContainer.OnTabChanged += OnTabChanged;
+
+ // Set UI toggles
+ ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage);
+ ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage);
+ ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc);
+ }
+
+ public void SetEntity(EntityUid uid)
+ {
+ Entity = uid;
// Pass owner to nav map
- NavMap.Owner = _owner;
+ NavMap.Owner = uid;
// Set nav map grid uid
var stationName = Loc.GetString("power-monitoring-window-unknown-location");
- if (_entManager.TryGetComponent(owner, out var xform))
+ if (_entManager.TryGetComponent(uid, out var xform))
{
NavMap.MapUid = xform.GridUid;
- // Assign station name
+ // Assign station name
if (_entManager.TryGetComponent(xform.GridUid, out var stationMetaData))
stationName = stationMetaData.EntityName;
var msg = new FormattedMessage();
- msg.AddMarkup(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName)));
+ msg.AddMarkupOrThrow(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName)));
StationName.SetMessage(msg);
}
@@ -68,29 +92,6 @@ public PowerMonitoringWindow(PowerMonitoringConsoleBoundUserInterface userInterf
StationName.SetMessage(stationName);
NavMap.Visible = false;
}
-
- // Set trackable entity selected action
- NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
-
- // Update nav map
- NavMap.ForceNavMapUpdate();
-
- // Set UI tab titles
- MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources"));
- MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes"));
- MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation"));
- MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc"));
-
- // Track when the MasterTabContainer changes its tab
- MasterTabContainer.OnTabChanged += OnTabChanged;
-
- // Set UI toggles
- ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage);
- ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage);
- ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc);
-
- // Set power monitoring message action
- SendPowerMonitoringConsoleMessageAction += userInterface.SendPowerMonitoringConsoleMessage;
}
private void OnTabChanged(int tab)
@@ -113,10 +114,7 @@ public void ShowEntites
PowerMonitoringConsoleEntry[] focusLoads,
EntityCoordinates? monitorCoords)
{
- if (_owner == null)
- return;
-
- if (!_entManager.TryGetComponent(_owner.Value, out var console))
+ if (!_entManager.TryGetComponent(Entity, out var console))
return;
// Update power status text
@@ -161,13 +159,13 @@ public void ShowEntites
}
// Show monitor location
- var mon = _entManager.GetNetEntity(_owner);
+ var mon = _entManager.GetNetEntity(Entity);
- if (monitorCoords != null && mon != null)
+ if (monitorCoords != null && mon.IsValid())
{
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
var blip = new NavMapBlip(monitorCoords.Value, texture, Color.Cyan, true, false);
- NavMap.TrackedEntities[mon.Value] = blip;
+ NavMap.TrackedEntities[mon] = blip;
}
// If the entry group doesn't match the current tab, the data is out dated, do not use it
@@ -239,7 +237,7 @@ private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
if (netEntity == null)
return;
- if (!_entManager.TryGetComponent(_owner, out var console))
+ if (!_entManager.TryGetComponent(Entity, out var console))
return;
if (!console.PowerMonitoringDeviceMetaData.TryGetValue(netEntity.Value, out var metaData))
@@ -266,7 +264,7 @@ protected override void FrameUpdate(FrameEventArgs args)
{
AutoScrollToFocus();
- // Warning sign pulse
+ // Warning sign pulse
var lit = _gameTiming.RealTime.TotalSeconds % BlinkFrequency > BlinkFrequency / 2f;
SystemWarningPanel.Modulate = lit ? Color.White : new Color(178, 178, 178);
}
diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs
index d5425425a76..ef99b01855c 100644
--- a/Content.Client/RCD/AlignRCDConstruction.cs
+++ b/Content.Client/RCD/AlignRCDConstruction.cs
@@ -45,7 +45,7 @@ public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
_unalignedMouseCoords = ScreenToCursorGrid(mouseScreen);
MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager);
- var gridId = MouseCoords.GetGridUid(_entityManager);
+ var gridId = _transformSystem.GetGrid(MouseCoords);
if (!_entityManager.TryGetComponent(gridId, out var mapGrid))
return;
@@ -75,7 +75,7 @@ public override bool IsValidPosition(EntityCoordinates position)
if (!_entityManager.TryGetComponent(player, out var xform))
return false;
- if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange))
+ if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange))
{
InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0);
return false;
@@ -105,8 +105,8 @@ public override bool IsValidPosition(EntityCoordinates position)
if (currentState is not GameplayStateBase screen)
return false;
-
- var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem));
+
+ var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords));
// Determine if the RCD operation is valid or not
if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false))
diff --git a/Content.Client/RCD/RCDMenu.xaml.cs b/Content.Client/RCD/RCDMenu.xaml.cs
index 3eb0397a690..f0d27d6b1fb 100644
--- a/Content.Client/RCD/RCDMenu.xaml.cs
+++ b/Content.Client/RCD/RCDMenu.xaml.cs
@@ -20,31 +20,37 @@ public sealed partial class RCDMenu : RadialMenu
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
- private readonly SpriteSystem _spriteSystem;
- private readonly SharedPopupSystem _popup;
+ private SharedPopupSystem _popup;
+ private SpriteSystem _sprites;
public event Action>? SendRCDSystemMessageAction;
private EntityUid _owner;
- public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui)
+ public RCDMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
- _spriteSystem = _entManager.System();
_popup = _entManager.System();
+ _sprites = _entManager.System();
- _owner = owner;
+ OnChildAdded += AddRCDMenuButtonOnClickActions;
+ }
+
+ public void SetEntity(EntityUid uid)
+ {
+ _owner = uid;
+ Refresh();
+ }
+ public void Refresh()
+ {
// Find the main radial container
var main = FindControl("Main");
- if (main == null)
- return;
-
// Populate secondary radial containers
- if (!_entManager.TryGetComponent(owner, out var rcd))
+ if (!_entManager.TryGetComponent(_owner, out var rcd))
return;
foreach (var protoId in rcd.AvailablePrototypes)
@@ -56,14 +62,10 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui)
continue;
var parent = FindControl(proto.Category);
-
- if (parent == null)
- continue;
-
var tooltip = Loc.GetString(proto.SetName);
if ((proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject) &&
- proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto))
+ proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false))
{
tooltip = Loc.GetString(entProto.Name);
}
@@ -84,7 +86,7 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui)
{
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
- Texture = _spriteSystem.Frame0(proto.Sprite),
+ Texture = _sprites.Frame0(proto.Sprite),
TextureScale = new Vector2(2f, 2f),
};
@@ -112,11 +114,9 @@ public RCDMenu(EntityUid owner, RCDMenuBoundUserInterface bui)
// Set up menu actions
foreach (var child in Children)
+ {
AddRCDMenuButtonOnClickActions(child);
-
- OnChildAdded += AddRCDMenuButtonOnClickActions;
-
- SendRCDSystemMessageAction += bui.SendRCDSystemMessage;
+ }
}
private static string OopsConcat(string a, string b)
@@ -153,7 +153,7 @@ private void AddRCDMenuButtonOnClickActions(Control control)
var name = Loc.GetString(proto.SetName);
if (proto.Prototype != null &&
- _protoManager.TryIndex(proto.Prototype, out var entProto))
+ _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false))
name = entProto.Name;
msg = Loc.GetString("rcd-component-change-build-mode", ("name", name));
diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs
index a37dbcecf8c..1dd03626ae6 100644
--- a/Content.Client/RCD/RCDMenuBoundUserInterface.cs
+++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs
@@ -3,6 +3,7 @@
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Input;
+using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.RCD;
@@ -24,8 +25,9 @@ protected override void Open()
{
base.Open();
- _menu = new(Owner, this);
- _menu.OnClose += Close;
+ _menu = this.CreateWindow();
+ _menu.SetEntity(Owner);
+ _menu.SendRCDSystemMessageAction += SendRCDSystemMessage;
// Open the menu, centered on the mouse
var vpSize = _displayManager.ScreenSize;
@@ -34,16 +36,8 @@ protected override void Open()
public void SendRCDSystemMessage(ProtoId protoId)
{
- // A predicted message cannot be used here as the RCD UI is closed immediately
+ // 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/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
index 8d5607af2d0..9ec24fae0ef 100644
--- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
+++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs
@@ -116,7 +116,9 @@ private void RadiationQuery(IEye? currentEye)
var shaderInstance = _pulses[pulseEntity];
shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity);
shaderInstance.instance.Range = pulse.VisualRange;
- } else {
+ }
+ else
+ {
_pulses[pulseEntity].shd.Dispose();
_pulses.Remove(pulseEntity);
}
@@ -129,7 +131,7 @@ private bool PulseQualifies(EntityUid pulseEntity, MapCoordinates currentEyeLoc)
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);
+ && transformSystem.InRange(transformComponent.Coordinates, transformSystem.ToCoordinates(transformComponent.ParentUid, currentEyeLoc), MaxDist);
}
private sealed record RadiationShaderInstance(MapCoordinates CurrentMapCoords, float Range, TimeSpan Start, float Duration)
diff --git a/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
new file mode 100644
index 00000000000..29d6c635ebc
--- /dev/null
+++ b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -0,0 +1,23 @@
+using Content.Client.Radio.Ui;
+using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Radio.EntitySystems;
+
+public sealed class RadioDeviceSystem : EntitySystem
+{
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnAfterHandleState);
+ }
+
+ private void OnAfterHandleState(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ if (_ui.TryGetOpenUi(ent.Owner, IntercomUiKey.Key, out var bui))
+ bui.Update(ent);
+ }
+}
diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
index abbb1d58ec4..401e7edd44a 100644
--- a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
+++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
@@ -1,6 +1,8 @@
using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Radio.Ui;
@@ -19,7 +21,12 @@ protected override void Open()
{
base.Open();
- _menu = new();
+ _menu = this.CreateWindow();
+
+ if (EntMan.TryGetComponent(Owner, out IntercomComponent? intercom))
+ {
+ _menu.Update((Owner, intercom));
+ }
_menu.OnMicPressed += enabled =>
{
@@ -33,26 +40,10 @@ protected override void Open()
{
SendMessage(new SelectIntercomChannelMessage(channel));
};
-
- _menu.OnClose += Close;
- _menu.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Close();
}
- protected override void UpdateState(BoundUserInterfaceState state)
+ public void Update(Entity ent)
{
- base.UpdateState(state);
-
- if (state is not IntercomBoundUIState msg)
- return;
-
- _menu?.Update(msg);
+ _menu?.Update(ent);
}
}
diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
index 8b4b38753c1..20d2e4a3e54 100644
--- a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
+++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
@@ -1,8 +1,9 @@
using Content.Client.UserInterface.Controls;
-using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.Client.Radio.Ui;
@@ -26,29 +27,43 @@ public IntercomMenu()
SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed);
}
- public void Update(IntercomBoundUIState state)
+ public void Update(Entity entity)
{
- MicButton.Pressed = state.MicEnabled;
- SpeakerButton.Pressed = state.SpeakerEnabled;
+ MicButton.Pressed = entity.Comp.MicrophoneEnabled;
+ SpeakerButton.Pressed = entity.Comp.SpeakerEnabled;
+
+ MicButton.Disabled = entity.Comp.SupportedChannels.Count == 0;
+ SpeakerButton.Disabled = entity.Comp.SupportedChannels.Count == 0;
+ ChannelOptions.Disabled = entity.Comp.SupportedChannels.Count == 0;
ChannelOptions.Clear();
_channels.Clear();
- for (var i = 0; i < state.AvailableChannels.Count; i++)
+ for (var i = 0; i < entity.Comp.SupportedChannels.Count; i++)
{
- var channel = state.AvailableChannels[i];
- if (!_prototype.TryIndex(channel, out var prototype))
+ var channel = entity.Comp.SupportedChannels[i];
+ if (!_prototype.TryIndex(channel, out var prototype))
continue;
_channels.Add(channel);
ChannelOptions.AddItem(Loc.GetString(prototype.Name), i);
- if (channel == state.SelectedChannel)
+ if (channel == entity.Comp.CurrentChannel)
ChannelOptions.Select(i);
}
+
+ if (entity.Comp.SupportedChannels.Count == 0)
+ {
+ ChannelOptions.AddItem(Loc.GetString("intercom-options-none"), 0);
+ ChannelOptions.Select(0);
+ }
+
ChannelOptions.OnItemSelected += args =>
{
+ if (!_channels.TryGetValue(args.Id, out var proto))
+ return;
+
ChannelOptions.SelectId(args.Id);
- OnChannelSelected?.Invoke(_channels[args.Id]);
+ OnChannelSelected?.Invoke(proto);
};
}
}
diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Position.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Position.cs
index d00e319eed9..71561703d64 100644
--- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Position.cs
+++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Position.cs
@@ -1,4 +1,5 @@
using Content.Shared.Movement.Components;
+using Content.Shared.Station.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -164,14 +165,25 @@ private bool TryFindFallbackSpawn(out EntityCoordinates coords)
float? maxSize = null;
var gridQuery = EntityQueryEnumerator();
+ var stationFound = false;
while (gridQuery.MoveNext(out var uid, out var grid))
{
var size = grid.LocalAABB.Size.LengthSquared();
- if (maxSize == null || size > maxSize)
- {
- maxUid = (uid, grid);
- maxSize = size;
- }
+
+ var station = HasComp(uid);
+
+ //We want the first station grid to overwrite any previous non-station grids no matter the size, in case the vgroid was found first
+ if (maxSize is not null && size < maxSize && !(!stationFound && station))
+ continue;
+
+ if (!station && stationFound)
+ continue;
+
+ maxUid = (uid, grid);
+ maxSize = size;
+
+ if (station)
+ stationFound = true;
}
coords = new EntityCoordinates(maxUid ?? default, default);
diff --git a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs
index da008ca04c9..9641adb5b2d 100644
--- a/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs
+++ b/Content.Client/Research/UI/DiskConsoleBoundUserInterface.cs
@@ -1,6 +1,7 @@
using Content.Shared.Research;
using Content.Shared.Research.Components;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Research.UI
{
@@ -17,10 +18,7 @@ protected override void Open()
{
base.Open();
- _menu = new();
-
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
_menu.OnServerButtonPressed += () =>
{
@@ -32,14 +30,6 @@ protected override void Open()
};
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Close();
- }
-
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
diff --git a/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs b/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs
index 07dd35a0056..288445e4dea 100644
--- a/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs
+++ b/Content.Client/Research/UI/ResearchClientBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Research.Components;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Research.UI
{
@@ -16,10 +17,9 @@ public ResearchClientBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne
protected override void Open()
{
base.Open();
-
- _menu = new ResearchClientServerSelectionMenu(this);
- _menu.OnClose += Close;
- _menu.OpenCentered();
+ _menu = this.CreateWindow();
+ _menu.OnServerSelected += SelectServer;
+ _menu.OnServerDeselected += DeselectServer;
}
public void SelectServer(int serverId)
@@ -38,12 +38,5 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (state is not ResearchClientBoundInterfaceState rState) return;
_menu?.Populate(rState.ServerCount, rState.ServerNames, rState.ServerIds, rState.SelectedServerId);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing) return;
- _menu?.Dispose();
- }
}
}
diff --git a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
index ceaa965e59f..d10f8b39f48 100644
--- a/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
+++ b/Content.Client/Research/UI/ResearchClientServerSelectionMenu.xaml.cs
@@ -13,27 +13,26 @@ public sealed partial class ResearchClientServerSelectionMenu : DefaultWindow
private int[] _serverIds = Array.Empty();
private int _selectedServerId = -1;
- private ResearchClientBoundUserInterface Owner { get; }
+ public event Action? OnServerSelected;
+ public event Action? OnServerDeselected;
- public ResearchClientServerSelectionMenu(ResearchClientBoundUserInterface owner)
+ public ResearchClientServerSelectionMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- Owner = owner;
-
Servers.OnItemSelected += OnItemSelected;
Servers.OnItemDeselected += OnItemDeselected;
}
public void OnItemSelected(ItemList.ItemListSelectedEventArgs itemListSelectedEventArgs)
{
- Owner.SelectServer(_serverIds[itemListSelectedEventArgs.ItemIndex]);
+ OnServerSelected?.Invoke(_serverIds[itemListSelectedEventArgs.ItemIndex]);
}
public void OnItemDeselected(ItemList.ItemListDeselectedEventArgs itemListDeselectedEventArgs)
{
- Owner.DeselectServer();
+ OnServerDeselected?.Invoke();
}
public void Populate(int serverCount, string[] serverNames, int[] serverIds, int selectedServerId)
diff --git a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs
index f29e66baaeb..2895ada61fb 100644
--- a/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs
+++ b/Content.Client/Research/UI/ResearchConsoleBoundUserInterface.cs
@@ -1,6 +1,8 @@
using Content.Shared.Research.Components;
+using Content.Shared.Research.Prototypes;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
+using Robust.Shared.Prototypes;
namespace Content.Client.Research.UI;
@@ -20,7 +22,8 @@ protected override void Open()
var owner = Owner;
- _consoleMenu = new ResearchConsoleMenu(owner);
+ _consoleMenu = this.CreateWindow();
+ _consoleMenu.SetEntity(owner);
_consoleMenu.OnTechnologyCardPressed += id =>
{
@@ -31,10 +34,20 @@ protected override void Open()
{
SendMessage(new ConsoleServerSelectionMessage());
};
+ }
+
+ public override void OnProtoReload(PrototypesReloadedEventArgs args)
+ {
+ base.OnProtoReload(args);
+
+ if (!args.WasModified())
+ return;
- _consoleMenu.OnClose += Close;
+ if (State is not ResearchConsoleBoundInterfaceState rState)
+ return;
- _consoleMenu.OpenCentered();
+ _consoleMenu?.UpdatePanels(rState);
+ _consoleMenu?.UpdateInformationPanel(rState);
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -46,12 +59,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_consoleMenu?.UpdatePanels(castState);
_consoleMenu?.UpdateInformationPanel(castState);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _consoleMenu?.Dispose();
- }
}
diff --git a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
index b364b833124..eafbe75fbb9 100644
--- a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
+++ b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
@@ -25,14 +25,13 @@ public sealed partial class ResearchConsoleMenu : FancyWindow
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _player = default!;
- private readonly TechnologyDatabaseComponent? _technologyDatabase;
private readonly ResearchSystem _research;
private readonly SpriteSystem _sprite;
private readonly AccessReaderSystem _accessReader;
- public readonly EntityUid Entity;
+ public EntityUid Entity;
- public ResearchConsoleMenu(EntityUid entity)
+ public ResearchConsoleMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -40,21 +39,23 @@ public ResearchConsoleMenu(EntityUid entity)
_research = _entity.System();
_sprite = _entity.System();
_accessReader = _entity.System();
- Entity = entity;
ServerButton.OnPressed += _ => OnServerButtonPressed?.Invoke();
+ }
- _entity.TryGetComponent(entity, out _technologyDatabase);
+ public void SetEntity(EntityUid entity)
+ {
+ Entity = entity;
}
- public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
+ public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
{
TechnologyCardsContainer.Children.Clear();
var availableTech = _research.GetAvailableTechnologies(Entity);
SyncTechnologyList(AvailableCardsContainer, availableTech);
- if (_technologyDatabase == null)
+ if (!_entity.TryGetComponent(Entity, out TechnologyDatabaseComponent? database))
return;
// i can't figure out the spacing so here you go
@@ -66,7 +67,7 @@ public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
var hasAccess = _player.LocalEntity is not { } local ||
!_entity.TryGetComponent(Entity, out var access) ||
_accessReader.IsAllowed(local, Entity, access);
- foreach (var techId in _technologyDatabase.CurrentTechnologyCards)
+ foreach (var techId in database.CurrentTechnologyCards)
{
var tech = _prototype.Index(techId);
var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech, includeTier: false), state.Points, hasAccess);
@@ -74,39 +75,39 @@ public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
TechnologyCardsContainer.AddChild(cardControl);
}
- var unlockedTech = _technologyDatabase.UnlockedTechnologies.Select(x => _prototype.Index(x));
+ var unlockedTech = database.UnlockedTechnologies.Select(x => _prototype.Index(x));
SyncTechnologyList(UnlockedCardsContainer, unlockedTech);
}
public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state)
{
var amountMsg = new FormattedMessage();
- amountMsg.AddMarkup(Loc.GetString("research-console-menu-research-points-text",
+ amountMsg.AddMarkupOrThrow(Loc.GetString("research-console-menu-research-points-text",
("points", state.Points)));
ResearchAmountLabel.SetMessage(amountMsg);
- if (_technologyDatabase == null)
+ if (!_entity.TryGetComponent(Entity, out TechnologyDatabaseComponent? database))
return;
var disciplineText = Loc.GetString("research-discipline-none");
var disciplineColor = Color.Gray;
- if (_technologyDatabase.MainDiscipline != null)
+ if (database.MainDiscipline != null)
{
- var discipline = _prototype.Index(_technologyDatabase.MainDiscipline);
+ var discipline = _prototype.Index(database.MainDiscipline);
disciplineText = Loc.GetString(discipline.Name);
disciplineColor = discipline.Color;
}
var msg = new FormattedMessage();
- msg.AddMarkup(Loc.GetString("research-console-menu-main-discipline",
+ msg.AddMarkupOrThrow(Loc.GetString("research-console-menu-main-discipline",
("name", disciplineText), ("color", disciplineColor)));
MainDisciplineLabel.SetMessage(msg);
TierDisplayContainer.Children.Clear();
- foreach (var disciplineId in _technologyDatabase.SupportedDisciplines)
+ foreach (var disciplineId in database.SupportedDisciplines)
{
var discipline = _prototype.Index(disciplineId);
- var tier = _research.GetHighestDisciplineTier(_technologyDatabase, discipline);
+ var tier = _research.GetHighestDisciplineTier(database, discipline);
// don't show tiers with no available tech
if (tier == 0)
diff --git a/Content.Client/Research/UI/TechnologyCardControl.xaml.cs b/Content.Client/Research/UI/TechnologyCardControl.xaml.cs
index f547457203e..292ed0ebe0b 100644
--- a/Content.Client/Research/UI/TechnologyCardControl.xaml.cs
+++ b/Content.Client/Research/UI/TechnologyCardControl.xaml.cs
@@ -23,7 +23,7 @@ public TechnologyCardControl(TechnologyPrototype technology, IPrototypeManager p
DisciplineTexture.Texture = spriteSys.Frame0(discipline.Icon);
TechnologyNameLabel.Text = Loc.GetString(technology.Name);
var message = new FormattedMessage();
- message.AddMarkup(Loc.GetString("research-console-tier-discipline-info",
+ message.AddMarkupOrThrow(Loc.GetString("research-console-tier-discipline-info",
("tier", technology.Tier), ("color", discipline.Color), ("discipline", Loc.GetString(discipline.Name))));
TierLabel.SetMessage(message);
UnlocksLabel.SetMessage(description);
diff --git a/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs
index 6185979eee6..9a5159880f9 100644
--- a/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs
+++ b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Robotics;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Robotics.UI;
@@ -16,7 +17,9 @@ protected override void Open()
{
base.Open();
- _window = new RoboticsConsoleWindow(Owner);
+ _window = this.CreateWindow();
+ _window.SetEntity(Owner);
+
_window.OnDisablePressed += address =>
{
SendMessage(new RoboticsConsoleDisableMessage(address));
@@ -25,9 +28,6 @@ protected override void Open()
{
SendMessage(new RoboticsConsoleDestroyMessage(address));
};
- _window.OnClose += Close;
-
- _window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -37,14 +37,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
if (state is not RoboticsConsoleState cast)
return;
- _window?.UpdateState(cast);
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- _window?.Dispose();
+ _window.UpdateState(cast);
}
}
diff --git a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
index fc7b234bccc..87d7e62c392 100644
--- a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
+++ b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs
@@ -23,11 +23,12 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public Action? OnDisablePressed;
public Action? OnDestroyPressed;
- private Entity _console;
private string? _selected;
private Dictionary _cyborgs = new();
- public RoboticsConsoleWindow(EntityUid console)
+ public EntityUid Entity;
+
+ public RoboticsConsoleWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -35,9 +36,6 @@ public RoboticsConsoleWindow(EntityUid console)
_lock = _entMan.System();
_sprite = _entMan.System();
- _console = (console, _entMan.GetComponent(console), null);
- _entMan.TryGetComponent(_console, out _console.Comp2);
-
Cyborgs.OnItemSelected += args =>
{
if (Cyborgs[args.ItemIndex].Metadata is not string address)
@@ -66,6 +64,11 @@ public RoboticsConsoleWindow(EntityUid console)
DestroyButton.StyleClasses.Add(StyleBase.ButtonCaution);
}
+ public void SetEntity(EntityUid uid)
+ {
+ Entity = uid;
+ }
+
public void UpdateState(RoboticsConsoleState state)
{
_cyborgs = state.Cyborgs;
@@ -81,7 +84,7 @@ public void UpdateState(RoboticsConsoleState state)
PopulateData();
- var locked = _lock.IsLocked((_console, _console.Comp2));
+ var locked = _lock.IsLocked(Entity);
DangerZone.Visible = !locked;
LockedMessage.Visible = locked;
}
@@ -135,13 +138,19 @@ private void PopulateData()
// how the turntables
DisableButton.Disabled = !(data.HasBrain && data.CanDisable);
- DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- DestroyButton.Disabled = _timing.CurTime < _console.Comp1.NextDestroy;
+ if (_entMan.TryGetComponent(Entity, out RoboticsConsoleComponent? console))
+ {
+ DestroyButton.Disabled = _timing.CurTime < console.NextDestroy;
+ }
+ else
+ {
+ DestroyButton.Disabled = true;
+ }
}
}
diff --git a/Content.Client/Rotation/RotationVisualizerSystem.cs b/Content.Client/Rotation/RotationVisualizerSystem.cs
index 6105c10c803..6d3be4d1c05 100644
--- a/Content.Client/Rotation/RotationVisualizerSystem.cs
+++ b/Content.Client/Rotation/RotationVisualizerSystem.cs
@@ -23,8 +23,8 @@ private void OnAppearanceChange(EntityUid uid, RotationVisualsComponent componen
if (args.Sprite == null)
return;
- // If not defined, defaults to standing.
- _appearance.TryGetData(uid, RotationVisuals.RotationState, out var state, args.Component);
+ if (!_appearance.TryGetData(uid, RotationVisuals.RotationState, out var state, args.Component))
+ return;
switch (state)
{
diff --git a/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs b/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs
index 8f1723d1f22..fe48b042f3e 100644
--- a/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs
+++ b/Content.Client/Salvage/UI/SalvageExpeditionConsoleBoundUserInterface.cs
@@ -30,17 +30,9 @@ public SalvageExpeditionConsoleBoundUserInterface(EntityUid owner, Enum uiKey) :
protected override void Open()
{
base.Open();
- _window = new OfferingWindow();
+ _window = this.CreateWindow();
_window.Title = Loc.GetString("salvage-expedition-window-title");
- _window.OnClose += Close;
- _window?.OpenCenteredLeft();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- _window?.Dispose();
- _window = null;
+ _window.OpenCenteredLeft();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -110,12 +102,21 @@ protected override void UpdateState(BoundUserInterfaceState state)
offering.AddContent(new Label
{
- Text = faction,
+ Text = string.IsNullOrWhiteSpace(Loc.GetString(_protoManager.Index(faction).Description))
+ ? LogAndReturnDefaultFactionDescription(faction)
+ : Loc.GetString(_protoManager.Index(faction).Description),
FontColorOverride = StyleNano.NanoGold,
HorizontalAlignment = Control.HAlignment.Left,
Margin = new Thickness(0f, 0f, 0f, 5f),
});
+ string LogAndReturnDefaultFactionDescription(string faction)
+ {
+ Logger.Error($"Description is null or white space for SalvageFactionPrototype: {faction}");
+ return Loc.GetString(_protoManager.Index(faction).ID);
+ }
+
+
// Duration
offering.AddContent(new Label
{
@@ -140,12 +141,20 @@ protected override void UpdateState(BoundUserInterfaceState state)
offering.AddContent(new Label
{
- Text = Loc.GetString(_protoManager.Index(biome).ID),
+ Text = string.IsNullOrWhiteSpace(Loc.GetString(_protoManager.Index(biome).Description))
+ ? LogAndReturnDefaultBiomDescription(biome)
+ : Loc.GetString(_protoManager.Index(biome).Description),
FontColorOverride = StyleNano.NanoGold,
HorizontalAlignment = Control.HAlignment.Left,
Margin = new Thickness(0f, 0f, 0f, 5f),
});
+ string LogAndReturnDefaultBiomDescription(string biome)
+ {
+ Logger.Error($"Description is null or white space for SalvageBiomeModPrototype: {biome}");
+ return Loc.GetString(_protoManager.Index(biome).ID);
+ }
+
// Modifiers
offering.AddContent(new Label
{
diff --git a/Content.Client/Salvage/UI/SalvageMagnetBoundUserInterface.cs b/Content.Client/Salvage/UI/SalvageMagnetBoundUserInterface.cs
index be3ba0e046e..bee8092ea8e 100644
--- a/Content.Client/Salvage/UI/SalvageMagnetBoundUserInterface.cs
+++ b/Content.Client/Salvage/UI/SalvageMagnetBoundUserInterface.cs
@@ -21,13 +21,9 @@ protected override void Open()
{
base.Open();
- if (_window is null)
- {
- _window = new OfferingWindow();
- _window.Title = Loc.GetString("salvage-magnet-window-title");
- _window.OnClose += Close;
- _window.OpenCenteredLeft();
- }
+ _window = this.CreateWindow();
+ _window.Title = Loc.GetString("salvage-magnet-window-title");
+ _window.OpenCenteredLeft();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -112,15 +108,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window.AddOption(option);
}
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing)
- {
- _window?.Close();
- _window?.Dispose();
- }
- }
}
diff --git a/Content.Client/Sandbox/SandboxSystem.cs b/Content.Client/Sandbox/SandboxSystem.cs
index 6a1129bb75d..8a4c93fa354 100644
--- a/Content.Client/Sandbox/SandboxSystem.cs
+++ b/Content.Client/Sandbox/SandboxSystem.cs
@@ -17,6 +17,7 @@ public sealed class SandboxSystem : SharedSandboxSystem
[Dependency] private readonly IPlacementManager _placement = default!;
[Dependency] private readonly ContentEyeSystem _contentEye = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!;
private bool _sandboxEnabled;
public bool SandboxAllowed { get; private set; }
@@ -92,7 +93,7 @@ public bool Copy(ICommonSession? session, EntityCoordinates coords, EntityUid ui
&& EntityManager.TryGetComponent(uid, out MetaDataComponent? comp)
&& !comp.EntityDeleted)
{
- if (comp.EntityPrototype == null || comp.EntityPrototype.NoSpawn || comp.EntityPrototype.Abstract)
+ if (comp.EntityPrototype == null || comp.EntityPrototype.HideSpawnMenu || comp.EntityPrototype.Abstract)
return false;
if (_placement.Eraser)
@@ -109,7 +110,8 @@ public bool Copy(ICommonSession? session, EntityCoordinates coords, EntityUid ui
}
// Try copy tile.
- if (!_map.TryFindGridAt(coords.ToMap(EntityManager, _transform), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef))
+
+ if (!_map.TryFindGridAt(_transform.ToMapCoordinates(coords), out var gridUid, out var grid) || !_mapSystem.TryGetTileRef(gridUid, grid, coords, out var tileRef))
return false;
if (_placement.Eraser)
diff --git a/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs
index 086369aa264..b8b4fb8a746 100644
--- a/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs
+++ b/Content.Client/Shuttles/BUI/IFFConsoleBoundUserInterface.cs
@@ -3,6 +3,7 @@
using Content.Shared.Shuttles.Events;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
namespace Content.Client.Shuttles.BUI;
@@ -20,8 +21,7 @@ protected override void Open()
{
base.Open();
- _window = new IFFConsoleWindow();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.ShowIFF += SendIFFMessage;
_window.ShowVessel += SendVesselMessage;
_window.OpenCenteredLeft();
diff --git a/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs
index 4bd44a47a8e..f75759b042f 100644
--- a/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs
+++ b/Content.Client/Shuttles/BUI/RadarConsoleBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Shuttles.BUIStates;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using RadarConsoleWindow = Content.Client.Shuttles.UI.RadarConsoleWindow;
namespace Content.Client.Shuttles.BUI;
@@ -20,18 +21,7 @@ protected override void Open()
{
base.Open();
- _window = new RadarConsoleWindow();
- _window.OnClose += Close;
- _window.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- {
- _window?.Dispose();
- }
+ _window = this.CreateWindow();
}
protected override void UpdateState(BoundUserInterfaceState state)
diff --git a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
index af7b6055c80..e677181419e 100644
--- a/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
+++ b/Content.Client/Shuttles/BUI/ShuttleConsoleBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Shuttles.BUIStates;
using Content.Shared.Shuttles.Events;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
using Robust.Shared.Map;
namespace Content.Client.Shuttles.BUI;
@@ -19,9 +20,7 @@ public ShuttleConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owne
protected override void Open()
{
base.Open();
- _window = new ShuttleConsoleWindow();
- _window.OpenCentered();
- _window.OnClose += Close;
+ _window = this.CreateWindow();
_window.RequestFTL += OnFTLRequest;
_window.RequestBeaconFTL += OnFTLBeaconRequest;
diff --git a/Content.Client/Shuttles/FtlArrivalOverlay.cs b/Content.Client/Shuttles/FtlArrivalOverlay.cs
new file mode 100644
index 00000000000..f24a1e96488
--- /dev/null
+++ b/Content.Client/Shuttles/FtlArrivalOverlay.cs
@@ -0,0 +1,82 @@
+using System.Numerics;
+using Content.Shared.Shuttles.Components;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Shuttles;
+
+///
+/// Plays a visualization whenever a shuttle is arriving from FTL.
+///
+public sealed class FtlArrivalOverlay : Overlay
+{
+ public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
+
+ private EntityLookupSystem _lookups;
+ private SharedMapSystem _maps;
+ private SharedTransformSystem _transforms;
+ private SpriteSystem _sprites;
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _protos = default!;
+
+ private readonly HashSet> _visualizers = new();
+
+ private ShaderInstance _shader;
+
+ public FtlArrivalOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+ _lookups = _entManager.System();
+ _transforms = _entManager.System();
+ _maps = _entManager.System();
+ _sprites = _entManager.System();
+
+ _shader = _protos.Index("unshaded").Instance();
+ }
+
+ protected override bool BeforeDraw(in OverlayDrawArgs args)
+ {
+ _visualizers.Clear();
+ _lookups.GetEntitiesOnMap(args.MapId, _visualizers);
+
+ return _visualizers.Count > 0;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ args.WorldHandle.UseShader(_shader);
+
+ foreach (var (uid, comp) in _visualizers)
+ {
+ var grid = comp.Grid;
+
+ if (!_entManager.TryGetComponent(grid, out MapGridComponent? mapGrid))
+ continue;
+
+ var texture = _sprites.GetFrame(comp.Sprite, TimeSpan.FromSeconds(comp.Elapsed), loop: false);
+ comp.Elapsed += (float) _timing.FrameTime.TotalSeconds;
+
+ // Need to manually transform the viewport in terms of the visualizer entity as the grid isn't in position.
+ var (_, _, worldMatrix, invMatrix) = _transforms.GetWorldPositionRotationMatrixWithInv(uid);
+ args.WorldHandle.SetTransform(worldMatrix);
+ var localAABB = invMatrix.TransformBox(args.WorldBounds);
+
+ var tilesEnumerator = _maps.GetLocalTilesEnumerator(grid, mapGrid, localAABB);
+
+ while (tilesEnumerator.MoveNext(out var tile))
+ {
+ var bounds = _lookups.GetLocalBounds(tile, mapGrid.TileSize);
+
+ args.WorldHandle.DrawTextureRect(texture, bounds);
+ }
+ }
+
+ args.WorldHandle.UseShader(null);
+ args.WorldHandle.SetTransform(Matrix3x2.Identity);
+ }
+}
diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs
index c134b7157c4..eb9ec285f8c 100644
--- a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs
+++ b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs
@@ -35,9 +35,9 @@ public MapCoordinates GetMapCoordinates(IMapObject mapObj)
switch (mapObj)
{
case ShuttleBeaconObject beacon:
- return GetCoordinates(beacon.Coordinates).ToMap(EntityManager, XformSystem);
+ return XformSystem.ToMapCoordinates(GetCoordinates(beacon.Coordinates));
case ShuttleExclusionObject exclusion:
- return GetCoordinates(exclusion.Coordinates).ToMap(EntityManager, XformSystem);
+ return XformSystem.ToMapCoordinates(GetCoordinates(exclusion.Coordinates));
case GridMapObject grid:
var gridXform = Transform(grid.Entity);
diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
index d5154a87bef..73c11de2795 100644
--- a/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
+++ b/Content.Client/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
@@ -38,9 +38,8 @@ public bool EnableShuttlePosition
private bool _enableShuttlePosition;
private EmergencyShuttleOverlay? _overlay;
- public override void Initialize()
+ private void InitializeEmergency()
{
- base.Initialize();
SubscribeNetworkEvent(OnShuttlePosMessage);
}
diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.cs
new file mode 100644
index 00000000000..a2c048ff90e
--- /dev/null
+++ b/Content.Client/Shuttles/Systems/ShuttleSystem.cs
@@ -0,0 +1,21 @@
+using Robust.Client.Graphics;
+
+namespace Content.Client.Shuttles.Systems;
+
+public sealed partial class ShuttleSystem
+{
+ [Dependency] private readonly IOverlayManager _overlays = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ InitializeEmergency();
+ _overlays.AddOverlay(new FtlArrivalOverlay());
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ _overlays.RemoveOverlay();
+ }
+}
diff --git a/Content.Client/Shuttles/UI/MapScreen.xaml.cs b/Content.Client/Shuttles/UI/MapScreen.xaml.cs
index 10800b8c5f7..0d7df38b912 100644
--- a/Content.Client/Shuttles/UI/MapScreen.xaml.cs
+++ b/Content.Client/Shuttles/UI/MapScreen.xaml.cs
@@ -261,7 +261,7 @@ private void RebuildMapObjects()
ourMap = shuttleXform.MapID;
}
- while (mapComps.MoveNext(out var mapComp, out var mapXform, out var mapMetadata))
+ while (mapComps.MoveNext(out var mapUid, out var mapComp, out var mapXform, out var mapMetadata))
{
if (_console != null && !_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId, _console.Value))
{
@@ -311,7 +311,7 @@ private void RebuildMapObjects()
};
_mapHeadings.Add(mapComp.MapId, gridContents);
- foreach (var grid in _mapManager.GetAllMapGrids(mapComp.MapId))
+ foreach (var grid in _mapManager.GetAllGrids(mapComp.MapId))
{
_entManager.TryGetComponent(grid.Owner, out IFFComponent? iffComp);
@@ -327,8 +327,10 @@ private void RebuildMapObjects()
{
AddMapObject(mapComp.MapId, gridObj);
}
- else if (!_shuttles.IsBeaconMap(_mapManager.GetMapEntityId(mapComp.MapId)) && (iffComp == null ||
- (iffComp.Flags & IFFFlags.Hide) == 0x0))
+ // If we can show it then add it to pending.
+ else if (!_shuttles.IsBeaconMap(mapUid) && (iffComp == null ||
+ (iffComp.Flags & IFFFlags.Hide) == 0x0) &&
+ !gridObj.HideButton)
{
_pendingMapObjects.Add((mapComp.MapId, gridObj));
}
@@ -336,11 +338,17 @@ private void RebuildMapObjects()
foreach (var (beacon, _) in _shuttles.GetExclusions(mapComp.MapId, _exclusions))
{
+ if (beacon.HideButton)
+ continue;
+
_pendingMapObjects.Add((mapComp.MapId, beacon));
}
foreach (var (beacon, _) in _shuttles.GetBeacons(mapComp.MapId, _beacons))
{
+ if (beacon.HideButton)
+ continue;
+
_pendingMapObjects.Add((mapComp.MapId, beacon));
}
@@ -425,9 +433,6 @@ private void AddMapObject(MapId mapId, IMapObject mapObj)
var existing = _mapObjects.GetOrNew(mapId);
existing.Add(mapObj);
- if (mapObj.HideButton)
- return;
-
var gridContents = _mapHeadings[mapId];
var gridButton = new Button()
diff --git a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs
index 31f0eecad79..61ae0699266 100644
--- a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs
@@ -107,7 +107,7 @@ protected override void Draw(DrawingHandleScreen handle)
DrawCircles(handle);
var gridNent = EntManager.GetNetEntity(GridEntity);
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
- var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner);
+ var ourGridMatrix = _xformSystem.GetWorldMatrix(GridEntity.Value);
var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix);
diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
index 8bd4a338cb0..53ad4a0b23a 100644
--- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
@@ -519,7 +519,7 @@ private void AddMapObject(List edges, List verts, ValueList();
+ _menu.SetEntity(Owner);
_menu.BrainButtonPressed += () =>
{
@@ -41,10 +41,6 @@ protected override void Open()
{
SendMessage(new BorgRemoveModuleBuiMessage(EntMan.GetNetEntity(module)));
};
-
- _menu.OnClose += Close;
-
- _menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -55,12 +51,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
return;
_menu?.UpdateState(msg);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Dispose();
- }
}
diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml b/Content.Client/Silicons/Borgs/BorgMenu.xaml
index 7d8fd9fe57d..4cc2e41a8fb 100644
--- a/Content.Client/Silicons/Borgs/BorgMenu.xaml
+++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml
@@ -10,7 +10,7 @@
VerticalExpand="True">
-
+
diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs
index 474a83b4530..f6a861aa057 100644
--- a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs
+++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs
@@ -21,25 +21,33 @@ public sealed partial class BorgMenu : FancyWindow
public Action? NameChanged;
public Action? RemoveModuleButtonPressed;
- private readonly BorgChassisComponent? _chassis;
- public readonly EntityUid Entity;
public float AccumulatedTime;
private string _lastValidName;
private List _modules = new();
- public BorgMenu(EntityUid entity)
+ public EntityUid Entity;
+
+ public BorgMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- Entity = entity;
+ _lastValidName = NameLineEdit.Text;
- if (_entity.TryGetComponent(Entity, out var chassis))
- _chassis = chassis;
+ EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke();
+ BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke();
+ NameLineEdit.OnTextChanged += OnNameChanged;
+ NameLineEdit.OnTextEntered += OnNameEntered;
+ NameLineEdit.OnFocusExit += OnNameFocusExit;
+
+ UpdateBrainButton();
+ }
+
+ public void SetEntity(EntityUid entity)
+ {
+ Entity = entity;
BorgSprite.SetEntity(entity);
- ChargeBar.MaxValue = 1f;
- ChargeBar.Value = 1f;
if (_entity.TryGetComponent(Entity, out var nameIdentifierComponent))
{
@@ -55,17 +63,6 @@ public BorgMenu(EntityUid entity)
NameIdentifierLabel.Visible = false;
NameLineEdit.Text = _entity.GetComponent(Entity).EntityName;
}
-
- _lastValidName = NameLineEdit.Text;
-
- EjectBatteryButton.OnPressed += _ => EjectBatteryButtonPressed?.Invoke();
- BrainButton.OnPressed += _ => BrainButtonPressed?.Invoke();
-
- NameLineEdit.OnTextChanged += OnNameChanged;
- NameLineEdit.OnTextEntered += OnNameEntered;
- NameLineEdit.OnFocusExit += OnNameFocusExit;
-
- UpdateBrainButton();
}
protected override void FrameUpdate(FrameEventArgs args)
@@ -89,7 +86,7 @@ public void UpdateState(BorgBuiState state)
private void UpdateBrainButton()
{
- if (_chassis?.BrainEntity is { } brain)
+ if (_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis) && chassis.BrainEntity is { } brain)
{
BrainButton.Text = _entity.GetComponent(brain).EntityName;
BrainView.Visible = true;
@@ -108,17 +105,17 @@ private void UpdateBrainButton()
private void UpdateModulePanel()
{
- if (_chassis == null)
+ if (!_entity.TryGetComponent(Entity, out BorgChassisComponent? chassis))
return;
ModuleCounter.Text = Loc.GetString("borg-ui-module-counter",
- ("actual", _chassis.ModuleCount),
- ("max", _chassis.MaxModules));
+ ("actual", chassis.ModuleCount),
+ ("max", chassis.MaxModules));
- if (_chassis.ModuleContainer.Count == _modules.Count)
+ if (chassis.ModuleContainer.Count == _modules.Count)
{
var isSame = true;
- foreach (var module in _chassis.ModuleContainer.ContainedEntities)
+ foreach (var module in chassis.ModuleContainer.ContainedEntities)
{
if (_modules.Contains(module))
continue;
@@ -132,7 +129,7 @@ private void UpdateModulePanel()
ModuleContainer.Children.Clear();
_modules.Clear();
- foreach (var module in _chassis.ModuleContainer.ContainedEntities)
+ foreach (var module in chassis.ModuleContainer.ContainedEntities)
{
var control = new BorgModuleControl(module, _entity);
control.RemoveButtonPressed += () =>
diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml
new file mode 100644
index 00000000000..1bcac4bca65
--- /dev/null
+++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs
new file mode 100644
index 00000000000..2e44b820df9
--- /dev/null
+++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawContainer.xaml.cs
@@ -0,0 +1,61 @@
+using Content.Shared.Silicons.Laws;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Silicons.Laws.SiliconLawEditUi;
+
+[GenerateTypedNameReferences]
+public sealed partial class SiliconLawContainer : BoxContainer
+{
+ public const string StyleClassSiliconLawPositionLabel = "SiliconLawPositionLabel";
+
+ public static readonly string CorruptedString =
+ Loc.GetString("ion-storm-law-scrambled-number", ("length", 5));
+
+ private SiliconLaw? _law;
+
+ public event Action? MoveLawUp;
+ public event Action? MoveLawDown;
+ public event Action? DeleteAction;
+
+
+ public SiliconLawContainer()
+ {
+ RobustXamlLoader.Load(this);
+
+ MoveUp.OnPressed += _ => MoveLawUp?.Invoke(_law!);
+ MoveDown.OnPressed += _ => MoveLawDown?.Invoke(_law!);
+ Corrupted.OnPressed += _ =>
+ {
+ if (Corrupted.Pressed)
+ {
+ _law!.LawIdentifierOverride = CorruptedString;
+ }
+ else
+ {
+ _law!.LawIdentifierOverride = null;
+ }
+ };
+
+ LawContent.OnTextChanged += _ => _law!.LawString = Rope.Collapse(LawContent.TextRope).Trim();
+ LawContent.Placeholder = new Rope.Leaf(Loc.GetString("silicon-law-ui-placeholder"));
+ Delete.OnPressed += _ => DeleteAction?.Invoke(_law!);
+ }
+
+ public void SetLaw(SiliconLaw law)
+ {
+ _law = law;
+ LawContent.TextRope = new Rope.Leaf(Loc.GetString(law.LawString));
+ PositionText.Text = law.Order.ToString();
+ if (!string.IsNullOrEmpty(law.LawIdentifierOverride))
+ {
+ Corrupted.Pressed = true;
+ }
+ else
+ {
+ Corrupted.Pressed = false;
+ }
+ }
+}
diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs
new file mode 100644
index 00000000000..a4d59d1f315
--- /dev/null
+++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs
@@ -0,0 +1,38 @@
+using Content.Client.Eui;
+using Content.Shared.Eui;
+using Content.Shared.Silicons.Laws;
+
+namespace Content.Client.Silicons.Laws.SiliconLawEditUi;
+
+public sealed class SiliconLawEui : BaseEui
+{
+ public readonly EntityManager _entityManager = default!;
+
+ private SiliconLawUi _siliconLawUi;
+ private EntityUid _target;
+
+ public SiliconLawEui()
+ {
+ _entityManager = IoCManager.Resolve();
+
+ _siliconLawUi = new SiliconLawUi();
+ _siliconLawUi.OnClose += () => SendMessage(new CloseEuiMessage());
+ _siliconLawUi.Save.OnPressed += _ => SendMessage(new SiliconLawsSaveMessage(_siliconLawUi.GetLaws(), _entityManager.GetNetEntity(_target)));
+ }
+
+ public override void HandleState(EuiStateBase state)
+ {
+ if (state is not SiliconLawsEuiState s)
+ {
+ return;
+ }
+
+ _target = _entityManager.GetEntity(s.Target);
+ _siliconLawUi.SetLaws(s.Laws);
+ }
+
+ public override void Opened()
+ {
+ _siliconLawUi.OpenCentered();
+ }
+}
diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml
new file mode 100644
index 00000000000..19dcbac6202
--- /dev/null
+++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml
@@ -0,0 +1,22 @@
+
+
+ this shit does not layout properly unless I put the horizontal boxcontainer inside of a vertical one
+ ????
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs
new file mode 100644
index 00000000000..372961ea9a3
--- /dev/null
+++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawUi.xaml.cs
@@ -0,0 +1,89 @@
+using System.Linq;
+using Content.Client.UserInterface.Controls;
+using Content.Shared.FixedPoint;
+using Content.Shared.Silicons.Laws;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Silicons.Laws.SiliconLawEditUi;
+
+[GenerateTypedNameReferences]
+public sealed partial class SiliconLawUi : FancyWindow
+{
+ private List _laws = new();
+
+ public SiliconLawUi()
+ {
+ RobustXamlLoader.Load(this);
+ NewLawButton.OnPressed += _ => AddNewLaw();
+ }
+
+ private void AddNewLaw()
+ {
+ var newLaw = new SiliconLaw();
+ newLaw.Order = FixedPoint2.New(_laws.Count + 1);
+ _laws.Add(newLaw);
+ SetLaws(_laws);
+ }
+
+ public void SetLaws(List sLaws)
+ {
+ _laws = sLaws;
+ LawContainer.RemoveAllChildren();
+ foreach (var law in sLaws.OrderBy(l => l.Order))
+ {
+ var lawControl = new SiliconLawContainer();
+ lawControl.SetLaw(law);
+ lawControl.MoveLawDown += MoveLawDown;
+ lawControl.MoveLawUp += MoveLawUp;
+ lawControl.DeleteAction += DeleteLaw;
+
+ LawContainer.AddChild(lawControl);
+ }
+ }
+
+ public void DeleteLaw(SiliconLaw law)
+ {
+ _laws.Remove(law);
+ SetLaws(_laws);
+ }
+
+ public void MoveLawDown(SiliconLaw law)
+ {
+ if (_laws.Count == 0)
+ {
+ return;
+ }
+
+ var index = _laws.IndexOf(law);
+ if (index == -1)
+ {
+ return;
+ }
+
+ _laws[index].Order += FixedPoint2.New(1);
+ SetLaws(_laws);
+ }
+
+ public void MoveLawUp(SiliconLaw law)
+ {
+ if (_laws.Count == 0)
+ {
+ return;
+ }
+
+ var index = _laws.IndexOf(law);
+ if (index == -1)
+ {
+ return;
+ }
+
+ _laws[index].Order += FixedPoint2.New(-1);
+ SetLaws(_laws);
+ }
+
+ public List GetLaws()
+ {
+ return _laws;
+ }
+}
diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs
index d150735fa11..56216b91847 100644
--- a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs
+++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs
@@ -2,6 +2,7 @@
using Content.Shared.Silicons.Laws;
using Content.Shared.Silicons.Laws.Components;
using JetBrains.Annotations;
+using Robust.Client.UserInterface;
namespace Content.Client.Silicons.Laws.Ui;
@@ -22,18 +23,7 @@ protected override void Open()
{
base.Open();
- _menu = new();
-
- _menu.OnClose += Close;
- _menu.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Close();
+ _menu = this.CreateWindow();
}
protected override void UpdateState(BoundUserInterfaceState state)
diff --git a/Content.Client/Singularity/SingularityOverlay.cs b/Content.Client/Singularity/SingularityOverlay.cs
index c7c6aa2b5b4..e71b6e96b85 100644
--- a/Content.Client/Singularity/SingularityOverlay.cs
+++ b/Content.Client/Singularity/SingularityOverlay.cs
@@ -100,6 +100,8 @@ protected override void Draw(in OverlayDrawArgs args)
///
private void OnProjectFromScreenToMap(ref PixelToMapEvent args)
{ // Mostly copypasta from the singularity shader.
+ if (args.Viewport.Eye == null)
+ return;
var maxDistance = MaxDistance * EyeManager.PixelsPerMeter;
var finalCoords = args.VisiblePosition;
@@ -112,10 +114,11 @@ private void OnProjectFromScreenToMap(ref PixelToMapEvent args)
// and in local space 'Y' is measured in pixels from the top of the viewport.
// As a minor optimization the locations of the singularities are transformed into fragment space in BeforeDraw so the shader doesn't need to.
// We need to undo that here or this will transform the cursor position as if the singularities were mirrored vertically relative to the center of the viewport.
+
var localPosition = _positions[i];
localPosition.Y = args.Viewport.Size.Y - localPosition.Y;
var delta = args.VisiblePosition - localPosition;
- var distance = (delta / args.Viewport.RenderScale).Length();
+ var distance = (delta / (args.Viewport.RenderScale * args.Viewport.Eye.Scale)).Length();
var deformation = _intensities[i] / MathF.Pow(distance, _falloffPowers[i]);
diff --git a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs
index e8442d23908..7d6a6cf2a5a 100644
--- a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs
+++ b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs
@@ -1,6 +1,6 @@
using Content.Shared.SprayPainter;
using Content.Shared.SprayPainter.Components;
-using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.SprayPainter.UI;
@@ -10,9 +10,6 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface
[ViewVariables]
private SprayPainterWindow? _window;
- [ViewVariables]
- private SprayPainterSystem? _painter;
-
public SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -21,27 +18,15 @@ protected override void Open()
{
base.Open();
- if (!EntMan.TryGetComponent(Owner, out var comp))
- return;
-
- _window = new SprayPainterWindow();
+ _window = this.CreateWindow();
- _painter = EntMan.System();
-
- _window.OnClose += Close;
_window.OnSpritePicked = OnSpritePicked;
_window.OnColorPicked = OnColorPicked;
- _window.Populate(_painter.Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
-
- _window.OpenCentered();
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- _window?.Dispose();
+ if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? comp))
+ {
+ _window.Populate(EntMan.System().Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
+ }
}
private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args)
diff --git a/Content.Client/Sprite/ContentSpriteSystem.cs b/Content.Client/Sprite/ContentSpriteSystem.cs
new file mode 100644
index 00000000000..da0547666b2
--- /dev/null
+++ b/Content.Client/Sprite/ContentSpriteSystem.cs
@@ -0,0 +1,218 @@
+using System.IO;
+using System.Numerics;
+using System.Threading;
+using System.Threading.Tasks;
+using Content.Client.Administration.Managers;
+using Content.Shared.Database;
+using Content.Shared.Verbs;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface;
+using Robust.Shared.ContentPack;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using Color = Robust.Shared.Maths.Color;
+
+namespace Content.Client.Sprite;
+
+public sealed class ContentSpriteSystem : EntitySystem
+{
+ [Dependency] private readonly IClientAdminManager _adminManager = default!;
+ [Dependency] private readonly IClyde _clyde = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IResourceManager _resManager = default!;
+ [Dependency] private readonly IUserInterfaceManager _ui = default!;
+
+ private ContentSpriteControl _control = new();
+
+ public static readonly ResPath Exports = new ResPath("/Exports");
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _resManager.UserData.CreateDir(Exports);
+ _ui.RootControl.AddChild(_control);
+ SubscribeLocalEvent>(GetVerbs);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+
+ foreach (var queued in _control._queuedTextures)
+ {
+ queued.Tcs.SetCanceled();
+ }
+
+ _control._queuedTextures.Clear();
+
+ _ui.RootControl.RemoveChild(_control);
+ }
+
+ ///
+ /// Exports sprites for all directions
+ ///
+ public async Task Export(EntityUid entity, bool includeId = true, CancellationToken cancelToken = default)
+ {
+ var tasks = new Task[4];
+ var i = 0;
+
+ foreach (var dir in new Direction[]
+ {
+ Direction.South,
+ Direction.East,
+ Direction.North,
+ Direction.West,
+ })
+ {
+ tasks[i++] = Export(entity, dir, includeId: includeId, cancelToken);
+ }
+
+ await Task.WhenAll(tasks);
+ }
+
+ ///
+ /// Exports the sprite for a particular direction.
+ ///
+ public async Task Export(EntityUid entity, Direction direction, bool includeId = true, CancellationToken cancelToken = default)
+ {
+ if (!_timing.IsFirstTimePredicted)
+ return;
+
+ if (!TryComp(entity, out SpriteComponent? spriteComp))
+ return;
+
+ // Don't want to wait for engine pr
+ var size = Vector2i.Zero;
+
+ foreach (var layer in spriteComp.AllLayers)
+ {
+ if (!layer.Visible)
+ continue;
+
+ size = Vector2i.ComponentMax(size, layer.PixelSize);
+ }
+
+ // Stop asserts
+ if (size.Equals(Vector2i.Zero))
+ return;
+
+ var texture = _clyde.CreateRenderTarget(new Vector2i(size.X, size.Y), new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "export");
+ var tcs = new TaskCompletionSource(cancelToken);
+
+ _control._queuedTextures.Enqueue((texture, direction, entity, includeId, tcs));
+
+ await tcs.Task;
+ }
+
+ private void GetVerbs(GetVerbsEvent ev)
+ {
+ if (!_adminManager.IsAdmin())
+ return;
+
+ Verb verb = new()
+ {
+ Text = Loc.GetString("export-entity-verb-get-data-text"),
+ Category = VerbCategory.Debug,
+ Act = () =>
+ {
+ Export(ev.Target);
+ },
+ };
+
+ ev.Verbs.Add(verb);
+ }
+
+ ///
+ /// This is horrible. I asked PJB if there's an easy way to render straight to a texture outside of the render loop
+ /// and she also mentioned this as a bad possibility.
+ ///
+ private sealed class ContentSpriteControl : Control
+ {
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly ILogManager _logMan = default!;
+ [Dependency] private readonly IResourceManager _resManager = default!;
+
+ internal Queue<(
+ IRenderTexture Texture,
+ Direction Direction,
+ EntityUid Entity,
+ bool IncludeId,
+ TaskCompletionSource Tcs)> _queuedTextures = new();
+
+ private ISawmill _sawmill;
+
+ public ContentSpriteControl()
+ {
+ IoCManager.InjectDependencies(this);
+ _sawmill = _logMan.GetSawmill("sprite.export");
+ }
+
+ protected override void Draw(DrawingHandleScreen handle)
+ {
+ base.Draw(handle);
+
+ while (_queuedTextures.TryDequeue(out var queued))
+ {
+ if (queued.Tcs.Task.IsCanceled)
+ continue;
+
+ try
+ {
+ if (!_entManager.TryGetComponent(queued.Entity, out MetaDataComponent? metadata))
+ continue;
+
+ var filename = metadata.EntityName;
+ var result = queued;
+
+ handle.RenderInRenderTarget(queued.Texture, () =>
+ {
+ handle.DrawEntity(result.Entity, result.Texture.Size / 2, Vector2.One, Angle.Zero,
+ overrideDirection: result.Direction);
+ }, Color.Transparent);
+
+ ResPath fullFileName;
+
+ if (queued.IncludeId)
+ {
+ fullFileName = Exports / $"{filename}-{queued.Direction}-{queued.Entity}.png";
+ }
+ else
+ {
+ fullFileName = Exports / $"{filename}-{queued.Direction}.png";
+ }
+
+ queued.Texture.CopyPixelsToMemory(image =>
+ {
+ if (_resManager.UserData.Exists(fullFileName))
+ {
+ _sawmill.Info($"Found existing file {fullFileName} to replace.");
+ _resManager.UserData.Delete(fullFileName);
+ }
+
+ using var file =
+ _resManager.UserData.Open(fullFileName, FileMode.CreateNew, FileAccess.Write,
+ FileShare.None);
+
+ image.SaveAsPng(file);
+ });
+
+ _sawmill.Info($"Saved screenshot to {fullFileName}");
+ queued.Tcs.SetResult();
+ }
+ catch (Exception exc)
+ {
+ queued.Texture.Dispose();
+
+ if (!string.IsNullOrEmpty(exc.StackTrace))
+ _sawmill.Fatal(exc.StackTrace);
+
+ queued.Tcs.SetException(exc);
+ }
+ }
+ }
+ }
+}
diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs
index c081581338f..7e681aeba3e 100644
--- a/Content.Client/Stack/StackSystem.cs
+++ b/Content.Client/Stack/StackSystem.cs
@@ -44,7 +44,7 @@ public override void SetCount(EntityUid uid, int amount, StackComponent? compone
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0 && !component.Lingering)
{
- Xform.DetachParentToNull(uid, Transform(uid));
+ Xform.DetachEntity(uid, Transform(uid));
return;
}
diff --git a/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs b/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs
index 720a2efb9dd..e7bab71e38e 100644
--- a/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs
+++ b/Content.Client/StationRecords/GeneralStationRecordConsoleBoundUserInterface.cs
@@ -1,4 +1,5 @@
using Content.Shared.StationRecords;
+using Robust.Client.UserInterface;
namespace Content.Client.StationRecords;
@@ -15,15 +16,12 @@ protected override void Open()
{
base.Open();
- _window = new();
+ _window = this.CreateWindow();
_window.OnKeySelected += key =>
SendMessage(new SelectStationRecord(key));
_window.OnFiltersChanged += (type, filterValue) =>
SendMessage(new SetStationRecordFilter(type, filterValue));
_window.OnDeleted += id => SendMessage(new DeleteStationRecord(id));
- _window.OnClose += Close;
-
- _window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
@@ -35,11 +33,4 @@ protected override void UpdateState(BoundUserInterfaceState state)
_window?.UpdateState(cast);
}
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- _window?.Close();
- }
}
diff --git a/Content.Client/Stealth/StealthSystem.cs b/Content.Client/Stealth/StealthSystem.cs
index 0b94e41f6bc..adb25e1ef63 100644
--- a/Content.Client/Stealth/StealthSystem.cs
+++ b/Content.Client/Stealth/StealthSystem.cs
@@ -11,6 +11,7 @@ namespace Content.Client.Stealth;
public sealed class StealthSystem : SharedStealthSystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
+ [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
private ShaderInstance _shader = default!;
@@ -81,7 +82,7 @@ private void OnShaderRender(EntityUid uid, StealthComponent component, BeforePos
if (!parent.IsValid())
return; // should never happen, but lets not kill the client.
var parentXform = Transform(parent);
- var reference = args.Viewport.WorldToLocal(parentXform.WorldPosition);
+ var reference = args.Viewport.WorldToLocal(_transformSystem.GetWorldPosition(parentXform));
reference.X = -reference.X;
var visibility = GetVisibility(uid, component);
diff --git a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs
index 8bf8ac56eab..f7764479c01 100644
--- a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs
+++ b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs
@@ -5,21 +5,26 @@ namespace Content.Client.Sticky.Visualizers;
public sealed class StickyVisualizerSystem : VisualizerSystem
{
+ private EntityQuery _spriteQuery;
+
public override void Initialize()
{
base.Initialize();
+
+ _spriteQuery = GetEntityQuery();
+
SubscribeLocalEvent(OnInit);
}
- private void OnInit(EntityUid uid, StickyVisualizerComponent component, ComponentInit args)
+ private void OnInit(Entity ent, ref ComponentInit args)
{
- if (!TryComp(uid, out SpriteComponent? sprite))
+ if (!_spriteQuery.TryComp(ent, out var sprite))
return;
- component.DefaultDrawDepth = sprite.DrawDepth;
+ ent.Comp.OriginalDrawDepth = sprite.DrawDepth;
}
- protected override void OnAppearanceChange(EntityUid uid, StickyVisualizerComponent component, ref AppearanceChangeEvent args)
+ protected override void OnAppearanceChange(EntityUid uid, StickyVisualizerComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
@@ -27,8 +32,7 @@ protected override void OnAppearanceChange(EntityUid uid, StickyVisualizerCompon
if (!AppearanceSystem.TryGetData(uid, StickyVisuals.IsStuck, out var isStuck, args.Component))
return;
- var drawDepth = isStuck ? component.StuckDrawDepth : component.DefaultDrawDepth;
+ var drawDepth = isStuck ? comp.StuckDrawDepth : comp.OriginalDrawDepth;
args.Sprite.DrawDepth = drawDepth;
-
}
}
diff --git a/Content.Client/Storage/Systems/ItemCounterSystem.cs b/Content.Client/Storage/Systems/ItemCounterSystem.cs
index 605f47d3b8a..fcb1ca17dc6 100644
--- a/Content.Client/Storage/Systems/ItemCounterSystem.cs
+++ b/Content.Client/Storage/Systems/ItemCounterSystem.cs
@@ -31,7 +31,7 @@ private void OnAppearanceChange(EntityUid uid, ItemCounterComponent comp, ref Ap
if (!_appearanceSystem.TryGetData(uid, StackVisuals.Hide, out var hidden, args.Component))
hidden = false;
-
+
if (comp.IsComposite)
ProcessCompositeSprite(uid, actual, maxCount, comp.LayerStates, hidden, sprite: args.Sprite);
else
diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs
index a74a118cd0f..eea7b9ec797 100644
--- a/Content.Client/Storage/Systems/StorageSystem.cs
+++ b/Content.Client/Storage/Systems/StorageSystem.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using System.Numerics;
using Content.Client.Animations;
using Content.Shared.Hands;
@@ -142,14 +142,14 @@ public void PickupAnimation(EntityUid item, EntityCoordinates initialCoords, Ent
{
if (!_timing.IsFirstTimePredicted)
return;
-
- if (finalCoords.InRange(EntityManager, TransformSystem, initialCoords, 0.1f) ||
+
+ if (TransformSystem.InRange(finalCoords, initialCoords, 0.1f) ||
!Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId))
{
return;
}
- var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem);
+ var finalMapPos = TransformSystem.ToMapCoordinates(finalCoords).Position;
var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId));
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
index 0010aedd964..7ed67f7b5dd 100644
--- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs
+++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs
@@ -2,6 +2,7 @@
using JetBrains.Annotations;
using System.Linq;
using Content.Shared.Store.Components;
+using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Store.Ui;
@@ -26,13 +27,10 @@ public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
protected override void Open()
{
- _menu = new StoreMenu();
+ _menu = this.CreateWindow();
if (EntMan.TryGetComponent(Owner, out var store))
_menu.Title = Loc.GetString(store.Name);
- _menu.OpenCentered();
- _menu.OnClose += Close;
-
_menu.OnListingButtonPressed += (_, listing) =>
{
SendMessage(new StoreBuyListingMessage(listing));
@@ -77,15 +75,6 @@ protected override void UpdateState(BoundUserInterfaceState state)
}
}
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _menu?.Close();
- _menu?.Dispose();
- }
-
private void UpdateListingsWithSearchFilter()
{
if (_menu == null)
diff --git a/Content.Client/Strip/StrippingMenu.cs b/Content.Client/Strip/StrippingMenu.cs
index eea867b7948..1c46b4be35c 100644
--- a/Content.Client/Strip/StrippingMenu.cs
+++ b/Content.Client/Strip/StrippingMenu.cs
@@ -1,4 +1,3 @@
-using Content.Client.Inventory;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Timing;
@@ -11,14 +10,12 @@ public sealed class StrippingMenu : DefaultWindow
public LayoutContainer InventoryContainer = new();
public BoxContainer HandsContainer = new() { Orientation = LayoutOrientation.Horizontal };
public BoxContainer SnareContainer = new();
- private StrippableBoundUserInterface _bui;
public bool Dirty = true;
- public StrippingMenu(string title, StrippableBoundUserInterface bui)
- {
- Title = title;
- _bui = bui;
+ public event Action? OnDirty;
+ public StrippingMenu()
+ {
var box = new BoxContainer() { Orientation = LayoutOrientation.Vertical, Margin = new Thickness(0, 8) };
Contents.AddChild(box);
box.AddChild(SnareContainer);
@@ -39,7 +36,7 @@ protected override void FrameUpdate(FrameEventArgs args)
return;
Dirty = false;
- _bui.UpdateMenu();
+ OnDirty?.Invoke();
}
}
}
diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs
index 4df332ec32d..fe3843c34a6 100644
--- a/Content.Client/Stylesheets/StyleNano.cs
+++ b/Content.Client/Stylesheets/StyleNano.cs
@@ -4,6 +4,7 @@
using Content.Client.Examine;
using Content.Client.PDA;
using Content.Client.Resources;
+using Content.Client.Silicons.Laws.SiliconLawEditUi;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Controls.FancyTree;
using Content.Client.Verbs.UI;
@@ -151,6 +152,11 @@ public sealed class StyleNano : StyleBase
public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD");
+ //Bwoink
+ public const string StyleClassPinButtonPinned = "pinButtonPinned";
+ public const string StyleClassPinButtonUnpinned = "pinButtonUnpinned";
+
+
public override Stylesheet Stylesheet { get; }
public StyleNano(IResourceCache resCache) : base(resCache)
@@ -1343,6 +1349,9 @@ public StyleNano(IResourceCache resCache) : base(resCache)
new StyleProperty(Label.StylePropertyAlignMode, Label.AlignMode.Center),
}),
+ Element().Class(OptionButton.StyleClassOptionsBackground)
+ .Prop(PanelContainer.StylePropertyPanel, new StyleBoxFlat(Color.FromHex("#25252A"))),
+
new StyleRule(new SelectorElement(typeof(PanelContainer), new []{ ClassHighDivider}, null, null), new []
{
new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = NanoGold, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2}),
@@ -1605,6 +1614,25 @@ public StyleNano(IResourceCache resCache) : base(resCache)
{
BackgroundColor = FancyTreeSelectedRowColor,
}),
+
+ // Silicon law edit ui
+ Element