diff --git a/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml b/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml
index f8ae37134a..7d22e1ef52 100644
--- a/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml
+++ b/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml
@@ -19,6 +19,7 @@
+
diff --git a/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml.cs b/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml.cs
index 25583b1e39..112be58634 100644
--- a/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml.cs
+++ b/Content.Client/EscapeMenu/UI/Tabs/GraphicsTab.xaml.cs
@@ -73,6 +73,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs
UpdateViewportScale();
};
+ ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled;
IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled;
ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
ApplyButton.OnPressed += OnApplyButtonPressed;
@@ -85,6 +86,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs
ViewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch);
IntegerScalingCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0;
ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender);
+ ShowHeldItemCheckBox.Pressed = _cfg.GetCVar(CCVars.HudHeldItemShow);
UpdateViewportScale();
UpdateApplyButton();
@@ -119,6 +121,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs
_cfg.SetCVar(CCVars.ViewportSnapToleranceMargin,
IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0);
_cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed);
+ _cfg.SetCVar(CCVars.HudHeldItemShow, ShowHeldItemCheckBox.Pressed);
_cfg.SaveToFile();
UpdateApplyButton();
}
@@ -145,6 +148,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs
var isVPScaleSame = (int) ViewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0);
var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender);
+ var isShowHeldItemSame = ShowHeldItemCheckBox.Pressed == _cfg.GetCVar(CCVars.HudHeldItemShow);
ApplyButton.Disabled = isVSyncSame &&
isFullscreenSame &&
@@ -154,7 +158,8 @@ namespace Content.Client.EscapeMenu.UI.Tabs
isVPScaleSame &&
isIntegerScalingSame &&
isVPResSame &&
- isHudThemeSame;
+ isHudThemeSame &&
+ isShowHeldItemSame;
}
private bool ConfigIsFullscreen =>
diff --git a/Content.Client/Hands/ShowHandItemOverlay.cs b/Content.Client/Hands/ShowHandItemOverlay.cs
new file mode 100644
index 0000000000..46cfb13ceb
--- /dev/null
+++ b/Content.Client/Hands/ShowHandItemOverlay.cs
@@ -0,0 +1,66 @@
+using Content.Shared.CCVar;
+using Robust.Client.Graphics;
+using Robust.Client.Input;
+using Robust.Shared;
+using Robust.Shared.Configuration;
+using Robust.Shared.Enums;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Maths;
+
+namespace Content.Client.Hands
+{
+ public sealed class ShowHandItemOverlay : Overlay
+ {
+ [Dependency] private readonly IConfigurationManager _cfg = default!;
+ [Dependency] private readonly IInputManager _inputManager = default!;
+ [Dependency] private readonly IClyde _clyde = default!;
+
+ private readonly IRenderTexture _renderBackbuffer;
+
+ public override OverlaySpace Space => OverlaySpace.ScreenSpace;
+
+ public ShowHandItemOverlay()
+ {
+ IoCManager.InjectDependencies(this);
+
+ _renderBackbuffer = _clyde.CreateRenderTarget(
+ (64, 64),
+ new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true),
+ new TextureSampleParameters
+ {
+ Filter = true
+ }, nameof(ShowHandItemOverlay));
+ }
+
+ protected override void DisposeBehavior()
+ {
+ base.DisposeBehavior();
+
+ _renderBackbuffer.Dispose();
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ var sys = EntitySystem.Get();
+ var handEntity = sys.GetActiveHandEntity();
+
+ if (handEntity == null || !_cfg.GetCVar(CCVars.HudHeldItemShow))
+ return;
+
+ var screen = args.ScreenHandle;
+ var halfSize = _renderBackbuffer.Size / 2;
+
+ screen.RenderInRenderTarget(_renderBackbuffer, () =>
+ {
+ screen.DrawEntity(handEntity, halfSize, new Vector2(1f, 1f) * _cfg.GetCVar(CVars.DisplayUIScale), Direction.South);
+ }, Color.Transparent);
+
+ var offset = _cfg.GetCVar(CCVars.HudHeldItemOffset);
+
+ var mousePos = _inputManager.MouseScreenPosition.Position;
+ screen.DrawTexture(_renderBackbuffer.Texture, mousePos - halfSize + offset, Color.White.WithAlpha(0.75f));
+ // screen.DrawRect(UIBox2.FromDimensions((offset, offset) + mousePos, (32, 32)), Color.Red);
+ }
+ }
+}
diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs
index c65b9e197f..fc98ea8d1c 100644
--- a/Content.Client/Hands/Systems/HandsSystem.cs
+++ b/Content.Client/Hands/Systems/HandsSystem.cs
@@ -67,9 +67,7 @@ namespace Content.Client.Hands
public HandsGuiState GetGuiState()
{
- var player = _playerManager.LocalPlayer?.ControlledEntity;
-
- if (player == null || !player.TryGetComponent(out HandsComponent? hands))
+ if (GetPlayerHandsComponent() is not { } hands)
return new HandsGuiState(Array.Empty());
var states = hands.Hands
@@ -79,6 +77,24 @@ namespace Content.Client.Hands
return new HandsGuiState(states, hands.ActiveHand);
}
+ public IEntity? GetActiveHandEntity()
+ {
+ if (GetPlayerHandsComponent() is not { ActiveHand: { } active } hands)
+ return null;
+
+ return hands.GetHand(active).HeldEntity;
+ }
+
+ private HandsComponent? GetPlayerHandsComponent()
+ {
+ var player = _playerManager.LocalPlayer?.ControlledEntity;
+
+ if (player == null || !player.TryGetComponent(out HandsComponent? hands))
+ return null;
+
+ return hands;
+ }
+
public void UIHandClick(HandsComponent hands, string handName)
{
if (!hands.TryGetHand(handName, out var pressedHand))
diff --git a/Content.Client/Viewport/GameScreen.cs b/Content.Client/Viewport/GameScreen.cs
index e8a93e17c0..97335ff30a 100644
--- a/Content.Client/Viewport/GameScreen.cs
+++ b/Content.Client/Viewport/GameScreen.cs
@@ -3,6 +3,7 @@ using Content.Client.Chat;
using Content.Client.Chat.Managers;
using Content.Client.Chat.UI;
using Content.Client.Construction.UI;
+using Content.Client.Hands;
using Content.Client.HUD;
using Content.Client.HUD.UI;
using Content.Client.Voting;
@@ -29,6 +30,7 @@ namespace Content.Client.Viewport
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IVoteManager _voteManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
+ [Dependency] private readonly IOverlayManager _overlayManager = default!;
[ViewVariables] private ChatBox? _gameChat;
private ConstructionMenuPresenter? _constructionMenu;
@@ -70,10 +72,13 @@ namespace Content.Client.Viewport
SetupPresenters();
_eyeManager.MainViewport = Viewport.Viewport;
+
+ _overlayManager.AddOverlay(new ShowHandItemOverlay());
}
public override void Shutdown()
{
+ _overlayManager.RemoveOverlay();
DisposePresenters();
base.Shutdown();
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index 0dbad615ae..a844896c50 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -245,6 +245,13 @@ namespace Content.Shared.CCVar
public static readonly CVarDef HudTheme =
CVarDef.Create("hud.theme", 0, CVar.ARCHIVE | CVar.CLIENTONLY);
+ public static readonly CVarDef HudHeldItemShow =
+ CVarDef.Create("hud.held_item_show", true, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+ public static readonly CVarDef HudHeldItemOffset =
+ CVarDef.Create("hud.held_item_offset", 28f, CVar.ARCHIVE | CVar.CLIENTONLY);
+
+
/*
* AI
*/
diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
index 1b7328b712..1122090f48 100644
--- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
+++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
@@ -18,6 +18,7 @@ ui-options-volume-percent = { TOSTRING($volume, "P0") }
## Graphics menu
+ui-options-show-held-item = Show held item next to cursor?
ui-options-vsync = VSync
ui-options-fullscreen = Fullscreen
ui-options-lighting-label = Lighting Quality: