diff --git a/Content.Client/Commands/HideMechanismsCommand.cs b/Content.Client/Commands/HideMechanismsCommand.cs new file mode 100644 index 0000000000..7123d72eea --- /dev/null +++ b/Content.Client/Commands/HideMechanismsCommand.cs @@ -0,0 +1,49 @@ +using Content.Shared.GameObjects.Components.Body.Mechanism; +using Robust.Client.Console; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.Console; +using Robust.Shared.Containers; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Client.Commands +{ + public class HideMechanismsCommand : IConsoleCommand + { + public string Command => "hidemechanisms"; + public string Description => $"Reverts the effects of {ShowMechanismsCommand.CommandName}"; + public string Help => $"{Command}"; + + public bool Execute(IDebugConsole console, params string[] args) + { + var componentManager = IoCManager.Resolve(); + var mechanisms = componentManager.EntityQuery(); + + foreach (var mechanism in mechanisms) + { + if (!mechanism.Owner.TryGetComponent(out SpriteComponent sprite)) + { + continue; + } + + sprite.ContainerOccluded = false; + + var tempParent = mechanism.Owner; + while (ContainerHelpers.TryGetContainer(tempParent, out var container)) + { + if (!container.ShowContents) + { + sprite.ContainerOccluded = true; + break; + } + + tempParent = container.Owner; + } + } + + IoCManager.Resolve().ProcessCommand("hidecontainedcontext"); + + return false; + } + } +} diff --git a/Content.Client/Commands/ShowMechanismsCommand.cs b/Content.Client/Commands/ShowMechanismsCommand.cs new file mode 100644 index 0000000000..86e18c33a9 --- /dev/null +++ b/Content.Client/Commands/ShowMechanismsCommand.cs @@ -0,0 +1,37 @@ +using Content.Shared.GameObjects.Components.Body.Mechanism; +using Robust.Client.Console; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.Console; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Client.Commands +{ + public class ShowMechanismsCommand : IConsoleCommand + { + public const string CommandName = "showmechanisms"; + + // ReSharper disable once StringLiteralTypo + public string Command => CommandName; + public string Description => "Makes mechanisms visible, even when they shouldn't be."; + public string Help => $"{Command}"; + + public bool Execute(IDebugConsole console, params string[] args) + { + var componentManager = IoCManager.Resolve(); + var mechanisms = componentManager.EntityQuery(); + + foreach (var mechanism in mechanisms) + { + if (mechanism.Owner.TryGetComponent(out SpriteComponent sprite)) + { + sprite.ContainerOccluded = false; + } + } + + IoCManager.Resolve().ProcessCommand("showcontainedcontext"); + + return false; + } + } +} diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs index 6ec02a898f..cf462e6287 100644 --- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs @@ -8,8 +8,10 @@ using Content.Client.UserInterface; using Content.Client.Utility; using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.Verbs; +using Content.Shared.GameTicking; using Content.Shared.Input; using JetBrains.Annotations; +using Robust.Client.GameObjects; using Robust.Client.GameObjects.EntitySystems; using Robust.Client.Graphics; using Robust.Client.Graphics.Drawing; @@ -38,7 +40,7 @@ using Timer = Robust.Shared.Timers.Timer; namespace Content.Client.GameObjects.EntitySystems { [UsedImplicitly] - public sealed class VerbSystem : SharedVerbSystem + public sealed class VerbSystem : SharedVerbSystem, IResettingEntitySystem { [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; @@ -56,12 +58,14 @@ namespace Content.Client.GameObjects.EntitySystems private bool IsAnyContextMenuOpen => _currentEntityList != null || _currentVerbListRoot != null; + private bool _playerCanSeeThroughContainers; public override void Initialize() { base.Initialize(); SubscribeNetworkEvent(FillEntityPopup); + SubscribeNetworkEvent(HandleContainerVisibilityMessage); IoCManager.InjectDependencies(this); @@ -77,6 +81,16 @@ namespace Content.Client.GameObjects.EntitySystems base.Shutdown(); } + public void Reset() + { + _playerCanSeeThroughContainers = false; + } + + private void HandleContainerVisibilityMessage(PlayerContainerVisibilityMessage ev) + { + _playerCanSeeThroughContainers = ev.CanSeeThrough; + } + public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates) { if (_currentVerbListRoot != null) @@ -99,6 +113,28 @@ namespace Content.Client.GameObjects.EntitySystems _currentVerbListRoot.Open(box); } + public bool CanSeeOnContextMenu(IEntity entity) + { + if (!entity.TryGetComponent(out SpriteComponent sprite) || !sprite.Visible) + { + return false; + } + + if (entity.GetAllComponents().Any(s => !s.ShowContextMenu(entity))) + { + return false; + } + + if (!_playerCanSeeThroughContainers && + ContainerHelpers.TryGetContainer(entity, out var container) && + !container.ShowContents) + { + return false; + } + + return true; + } + private bool OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args) { if (IsAnyContextMenuOpen) @@ -125,17 +161,7 @@ namespace Content.Client.GameObjects.EntitySystems var first = true; foreach (var entity in entities) { - if (!entity.TryGetComponent(out ISpriteComponent sprite) || !sprite.Visible) - { - continue; - } - - if (entity.GetAllComponents().Any(s => !s.ShowContextMenu(playerEntity))) - { - continue; - } - - if (ContainerHelpers.TryGetContainer(entity, out var container) && !container.ShowContents) + if (!CanSeeOnContextMenu(entity)) { continue; } diff --git a/Content.Server/Commands/HideContainedContextCommand.cs b/Content.Server/Commands/HideContainedContextCommand.cs new file mode 100644 index 0000000000..a4c7393146 --- /dev/null +++ b/Content.Server/Commands/HideContainedContextCommand.cs @@ -0,0 +1,26 @@ +#nullable enable +using Content.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.Commands +{ + public class HideContainedContextCommand : IClientCommand + { + public string Command => "hidecontainedcontext"; + public string Description => $"Reverts the effects of {ShowContainedContextCommand.CommandName}"; + public string Help => $"{Command}"; + + public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) + { + if (player == null) + { + shell.SendText(player, "You need to be a player to use this command."); + return; + } + + EntitySystem.Get().RemoveContainerVisibility(player); + } + } +} diff --git a/Content.Server/Commands/ShowContainedContextCommand.cs b/Content.Server/Commands/ShowContainedContextCommand.cs new file mode 100644 index 0000000000..3b37d6375e --- /dev/null +++ b/Content.Server/Commands/ShowContainedContextCommand.cs @@ -0,0 +1,29 @@ +#nullable enable +using Content.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.Commands +{ + public class ShowContainedContextCommand : IClientCommand + { + public const string CommandName = "showcontainedcontext"; + + // ReSharper disable once StringLiteralTypo + public string Command => CommandName; + public string Description => "Makes contained entities visible on the context menu, even when they shouldn't be."; + public string Help => $"{Command}"; + + public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) + { + if (player == null) + { + shell.SendText(player, "You need to be a player to use this command."); + return; + } + + EntitySystem.Get().AddContainerVisibility(player); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs index 1e2a1cec4e..396631aded 100644 --- a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs @@ -1,7 +1,12 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using System.Reflection; +using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.Verbs; +using Content.Shared.GameTicking; using Robust.Server.Interfaces.Player; +using Robust.Server.Player; +using Robust.Shared.Enums; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; @@ -10,18 +15,63 @@ using static Content.Shared.GameObjects.EntitySystemMessages.VerbSystemMessages; namespace Content.Server.GameObjects.EntitySystems { - public class VerbSystem : SharedVerbSystem + public class VerbSystem : SharedVerbSystem, IResettingEntitySystem { [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + private readonly HashSet _seesThroughContainers = new HashSet(); public override void Initialize() { base.Initialize(); + IoCManager.InjectDependencies(this); + SubscribeNetworkEvent(RequestVerbs); SubscribeNetworkEvent(UseVerb); - IoCManager.InjectDependencies(this); + _playerManager.PlayerStatusChanged += PlayerStatusChanged; + } + + private void PlayerStatusChanged(object? sender, SessionStatusEventArgs args) + { + if (args.NewStatus == SessionStatus.Disconnected) + { + _seesThroughContainers.Remove(args.Session); + } + } + + public void Reset() + { + _seesThroughContainers.Clear(); + } + + public void AddContainerVisibility(IPlayerSession session) + { + if (!_seesThroughContainers.Add(session)) + { + return; + } + + var message = new PlayerContainerVisibilityMessage(true); + RaiseNetworkEvent(message, session.ConnectedClient); + } + + public void RemoveContainerVisibility(IPlayerSession session) + { + if (!_seesThroughContainers.Remove(session)) + { + return; + } + + var message = new PlayerContainerVisibilityMessage(false); + RaiseNetworkEvent(message, session.ConnectedClient); + } + + public bool HasContainerVisibility(IPlayerSession session) + { + return _seesThroughContainers.Contains(session); } private void UseVerb(UseVerbMessage use, EntitySessionEventArgs eventArgs) diff --git a/Content.Shared/GameObjects/EntitySystemMessages/PlayerContainerVisibilityMessage.cs b/Content.Shared/GameObjects/EntitySystemMessages/PlayerContainerVisibilityMessage.cs new file mode 100644 index 0000000000..3795e5a14b --- /dev/null +++ b/Content.Shared/GameObjects/EntitySystemMessages/PlayerContainerVisibilityMessage.cs @@ -0,0 +1,17 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.EntitySystemMessages +{ + [Serializable, NetSerializable] + public class PlayerContainerVisibilityMessage : EntitySystemMessage + { + public readonly bool CanSeeThrough; + + public PlayerContainerVisibilityMessage(bool canSeeThrough) + { + CanSeeThrough = canSeeThrough; + } + } +} diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index 0ce7ef1073..916f74d526 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -109,6 +109,10 @@ - deleteewi - hurt - toggledisallowlatejoin + - showcontainedcontext + - hidecontainedcontext + - showmechanisms + - hidemechanisms - attachbodypart - attachtoself - attachtogrid @@ -215,6 +219,10 @@ - deleteewi - hurt - toggledisallowlatejoin + - showcontainedcontext + - hidecontainedcontext + - showmechanisms + - hidemechanisms - attachbodypart - attachtoself - attachtogrid