From bec1f99f1f1ccc12d0bc4ca75f9b54ce3fe03a6f Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 22 Nov 2021 09:40:09 +1300 Subject: [PATCH] Restrict examine/context-menu usage when incapacitated. (#5416) * wip * update in-view checks * cleanup --- .../ContextMenu/UI/EntityMenuPresenter.cs | 9 +-- Content.Client/Examine/ExamineSystem.cs | 22 +++--- Content.Client/Verbs/VerbSystem.cs | 27 ++++++-- Content.Shared/Examine/ExamineSystemShared.cs | 68 +++++++++++++------ 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/Content.Client/ContextMenu/UI/EntityMenuPresenter.cs b/Content.Client/ContextMenu/UI/EntityMenuPresenter.cs index 76dac3ae9e..d6bd1704a7 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuPresenter.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuPresenter.cs @@ -46,6 +46,7 @@ namespace Content.Client.ContextMenu.UI [Dependency] private readonly IEyeManager _eyeManager = default!; private readonly VerbSystem _verbSystem; + private readonly ExamineSystem _examineSystem; /// /// This maps the currently displayed entities to the actual GUI elements. @@ -60,6 +61,7 @@ namespace Content.Client.ContextMenu.UI IoCManager.InjectDependencies(this); _verbSystem = verbSystem; + _examineSystem = EntitySystem.Get(); _cfg.OnValueChanged(CCVars.EntityMenuGroupingType, OnGroupingChanged, true); @@ -157,10 +159,9 @@ namespace Content.Client.ContextMenu.UI var coords = args.Coordinates.ToMap(_entityManager); - if (!_verbSystem.TryGetEntityMenuEntities(coords, out var entities)) - return false; + if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities)) + OpenRootMenu(entities); - OpenRootMenu(entities); return true; } @@ -183,7 +184,7 @@ namespace Content.Client.ContextMenu.UI foreach (var entity in Elements.Keys.ToList()) { - if (entity.Deleted || !ignoreFov && !player.InRangeUnOccluded(entity)) + if (entity.Deleted || !ignoreFov && !_examineSystem.CanExamine(player, entity)) RemoveEntity(entity); } } diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index c44e9e8d7a..a226d0b5a1 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Input; using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Client.GameObjects; +using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -28,6 +29,7 @@ namespace Content.Client.Examine { [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IEyeManager _eyeManager = default!; public const string StyleClassEntityTooltip = "entity-tooltip"; @@ -52,17 +54,8 @@ namespace Content.Client.Examine if (_examineTooltipOpen == null || !_examineTooltipOpen.Visible) return; if (_examinedEntity == null || _playerEntity == null) return; - Ignored predicate = entity => entity == _playerEntity || entity == _examinedEntity; - - if (_playerEntity.TryGetContainer(out var container)) - { - predicate += entity => entity == container.Owner; - } - - if (!InRangeUnOccluded(_playerEntity, _examinedEntity, ExamineRange, predicate)) - { + if (!CanExamine(_playerEntity, _examinedEntity)) CloseTooltip(); - } } public override void Shutdown() @@ -71,6 +64,15 @@ namespace Content.Client.Examine base.Shutdown(); } + public override bool CanExamine(IEntity examiner, MapCoordinates target, Ignored? predicate = null) + { + var b = _eyeManager.GetWorldViewbounds(); + if (!b.Contains(target.Position)) + return false; + + return base.CanExamine(examiner, target, predicate); + } + private bool HandleExamine(ICommonSession? session, EntityCoordinates coords, EntityUid uid) { if (!uid.IsValid() || !EntityManager.TryGetEntity(uid, out var entity)) diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 1f518fdd70..b447b077cb 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -3,22 +3,25 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Client.ContextMenu.UI; +using Content.Client.Examine; using Content.Client.Popups; using Content.Client.Verbs.UI; +using Content.Client.Viewport; using Content.Shared.Examine; using Content.Shared.GameTicking; -using Content.Shared.Interaction.Helpers; using Content.Shared.Tag; using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Client.State; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; +using static Content.Shared.Interaction.SharedInteractionSystem; namespace Content.Client.Verbs { @@ -26,6 +29,8 @@ namespace Content.Client.Verbs public sealed class VerbSystem : SharedVerbSystem { [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly ExamineSystem _examineSystem = default!; + [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityLookup _entityLookup = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -85,20 +90,28 @@ namespace Content.Client.Verbs public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true)] out List? result) { result = null; - var player = _playerManager.LocalPlayer?.ControlledEntity; + if (_stateManager.CurrentState is not GameScreenBase gameScreenBase) + return false; + + var player = _playerManager.LocalPlayer?.ControlledEntity; if (player == null) return false; + // If FOV drawing is disabled, we will modify the visibility option to ignore visiblity checks. var visibility = _eyeManager.CurrentEye.DrawFov ? Visibility : Visibility | MenuVisibility.NoFov; - // Check if we have LOS to the clicked-location. - if ((visibility & MenuVisibility.NoFov) == 0 && - !player.InRangeUnOccluded(targetPos, range: ExamineSystemShared.ExamineRange)) - return false; - + // Do we have to do FoV checks? + if ((visibility & MenuVisibility.NoFov) == 0) + { + var entitiesUnderMouse = gameScreenBase.GetEntitiesUnderPosition(targetPos); + Ignored? predicate = e => e == player || entitiesUnderMouse.Contains(e); + if (!_examineSystem.CanExamine(player, targetPos, predicate)) + return false; + } + // Get entities var entities = _entityLookup.GetEntitiesInRange(targetPos.MapId, targetPos.Position, EntityMenuLookupSize) .ToList(); diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 8fd3f10134..cc390317d7 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Linq; using Content.Shared.DragDrop; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; +using Content.Shared.MobState.Components; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.GameObjects; @@ -27,12 +28,30 @@ namespace Content.Shared.Examine public abstract class ExamineSystemShared : EntitySystem { + /// + /// Examine range to use when the examiner is in critical condition. + /// + /// + /// Detailed examinations are disabled while incapactiated. Ideally this should just be set equal to the + /// radius of the crit overlay that blackens most of the screen. The actual radius of that is defined + /// in a shader sooo... eh. + /// + public const float CritExamineRange = 1.3f; + + /// + /// Examine range to use when the examiner is dead. See . + /// + public const float DeadExamineRange = 0.75f; + public const float ExamineRange = 16f; - public const float ExamineRangeSquared = ExamineRange * ExamineRange; protected const float ExamineDetailsRange = 3f; - private static bool IsInDetailsRange(IEntity examiner, IEntity entity) + private bool IsInDetailsRange(IEntity examiner, IEntity entity) { + // check if the mob is in ciritcal or dead + if (EntityManager.TryGetComponent(examiner.Uid, out MobStateComponent mobState) && mobState.IsIncapacitated()) + return false; + if (entity.TryGetContainerMan(out var man) && man.Owner == examiner) return true; @@ -41,38 +60,47 @@ namespace Content.Shared.Examine } [Pure] - protected static bool CanExamine(IEntity examiner, IEntity examined) + public bool CanExamine(IEntity examiner, IEntity examined) + { + return CanExamine(examiner, examined.Transform.MapPosition, + entity => entity == examiner || entity == examined); + } + + [Pure] + public virtual bool CanExamine(IEntity examiner, MapCoordinates target, Ignored? predicate = null) { if (!examiner.TryGetComponent(out ExaminerComponent? examinerComponent)) - { return false; - } if (!examinerComponent.DoRangeCheck) - { return true; - } - if (examiner.Transform.MapID != examined.Transform.MapID) - { + if (examiner.Transform.MapID != target.MapId) return false; - } - - Ignored predicate = entity => entity == examiner || entity == examined; - - if (examiner.TryGetContainer(out var container)) - { - predicate += entity => entity == container.Owner; - } return InRangeUnOccluded( examiner.Transform.MapPosition, - examined.Transform.MapPosition, - ExamineRange, + target, + GetExaminerRange(examiner.Uid), predicate: predicate, ignoreInsideBlocker: true); } + /// + /// Check if a given examiner is incapacitated. If yes, return a reduced examine range. Otherwise, return the deault range. + /// + public float GetExaminerRange(EntityUid examiner, MobStateComponent? mobState = null) + { + if (Resolve(examiner, ref mobState, logMissing: false)) + { + if (mobState.IsDead()) + return DeadExamineRange; + else if (mobState.IsCritical()) + return CritExamineRange; + } + return ExamineRange; + } + public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true) { if (origin.MapId == MapId.Nullspace ||