Restrict examine/context-menu usage when incapacitated. (#5416)
* wip * update in-view checks * cleanup
This commit is contained in:
@@ -46,6 +46,7 @@ namespace Content.Client.ContextMenu.UI
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
|
||||
private readonly VerbSystem _verbSystem;
|
||||
private readonly ExamineSystem _examineSystem;
|
||||
|
||||
/// <summary>
|
||||
/// 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<ExamineSystem>();
|
||||
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<IEntity>? 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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Examine range to use when the examiner is in critical condition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public const float CritExamineRange = 1.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Examine range to use when the examiner is dead. See <see cref="CritExamineRange"/>.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a given examiner is incapacitated. If yes, return a reduced examine range. Otherwise, return the deault range.
|
||||
/// </summary>
|
||||
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 ||
|
||||
|
||||
Reference in New Issue
Block a user