Entity menu lookup changes (#32395)
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Examine;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Popups;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -13,6 +13,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -30,11 +31,10 @@ namespace Content.Client.Verbs
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
/// <summary>
|
||||
/// When a user right clicks somewhere, how large is the box we use to get entities for the context menu?
|
||||
/// </summary>
|
||||
public const float EntityMenuLookupSize = 0.25f;
|
||||
private float _lookupSize;
|
||||
|
||||
/// <summary>
|
||||
/// These flags determine what entities the user can see on the context menu.
|
||||
@@ -43,128 +43,127 @@ namespace Content.Client.Verbs
|
||||
|
||||
public Action<VerbsResponseEvent>? OnVerbsResponse;
|
||||
|
||||
private List<EntityUid> _entities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<VerbsResponseEvent>(HandleVerbResponse);
|
||||
Subs.CVar(_cfg, CCVars.GameEntityMenuLookup, OnLookupChanged, true);
|
||||
}
|
||||
|
||||
private void OnLookupChanged(float val)
|
||||
{
|
||||
_lookupSize = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all of the entities in an area for displaying on the context menu.
|
||||
/// Get all of the entities in an area for displaying on the context menu.
|
||||
/// </summary>
|
||||
public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true)] out List<EntityUid>? result)
|
||||
/// <returns>True if any entities were found.</returns>
|
||||
public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true)] out List<EntityUid>? entities)
|
||||
{
|
||||
result = null;
|
||||
entities = null;
|
||||
|
||||
if (_stateManager.CurrentState is not GameplayStateBase gameScreenBase)
|
||||
if (_stateManager.CurrentState is not GameplayStateBase)
|
||||
return false;
|
||||
|
||||
var player = _playerManager.LocalEntity;
|
||||
if (player == null)
|
||||
if (_playerManager.LocalEntity is not { } player)
|
||||
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;
|
||||
var visibility = _eyeManager.CurrentEye.DrawFov ? Visibility : Visibility | MenuVisibility.NoFov;
|
||||
|
||||
var ev = new MenuVisibilityEvent()
|
||||
var ev = new MenuVisibilityEvent
|
||||
{
|
||||
TargetPos = targetPos,
|
||||
Visibility = visibility,
|
||||
};
|
||||
|
||||
RaiseLocalEvent(player.Value, ref ev);
|
||||
RaiseLocalEvent(player, ref ev);
|
||||
visibility = ev.Visibility;
|
||||
|
||||
// Get entities
|
||||
_entities.Clear();
|
||||
var entitiesUnderMouse = _tree.QueryAabb(targetPos.MapId, Box2.CenteredAround(targetPos.Position, new Vector2(EntityMenuLookupSize, EntityMenuLookupSize)));
|
||||
bool Predicate(EntityUid e) => e == player;
|
||||
// Initially, we include all entities returned by a sprite area lookup
|
||||
var box = Box2.CenteredAround(targetPos.Position, new Vector2(_lookupSize, _lookupSize));
|
||||
var queryResult = _tree.QueryAabb(targetPos.MapId, box);
|
||||
entities = new List<EntityUid>(queryResult.Count);
|
||||
foreach (var ent in queryResult)
|
||||
{
|
||||
entities.Add(ent.Uid);
|
||||
}
|
||||
|
||||
// If we're in a container list all other entities in it.
|
||||
// E.g., allow players in lockers to examine / interact with other entities in the same locker
|
||||
if (_containers.TryGetContainingContainer((player, null), out var container))
|
||||
{
|
||||
// Only include the container contents when clicking near it.
|
||||
if (entities.Contains(container.Owner)
|
||||
|| _containers.TryGetOuterContainer(container.Owner, Transform(container.Owner), out var outer)
|
||||
&& entities.Contains(outer.Owner))
|
||||
{
|
||||
// The container itself might be in some other container, so it might not have been added by the
|
||||
// sprite tree lookup.
|
||||
if (!entities.Contains(container.Owner))
|
||||
entities.Add(container.Owner);
|
||||
|
||||
// TODO Context Menu
|
||||
// This might miss entities in some situations. E.g., one of the contained entities entity in it, that
|
||||
// itself has another entity attached to it, then we should be able to "see" that entity.
|
||||
// E.g., if a security guard is on a segway and gets thrown in a locker, this wouldn't let you see the guard.
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
if (!entities.Contains(ent))
|
||||
entities.Add(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((visibility & MenuVisibility.InContainer) != 0)
|
||||
{
|
||||
// This is inefficient, but I'm lazy and CBF implementing my own recursive container method. Note that
|
||||
// this might actually fail to add the contained children of some entities in the menu. E.g., an entity
|
||||
// with a large sprite aabb, but small broadphase might appear in the menu, but have its children added
|
||||
// by this.
|
||||
var flags = LookupFlags.All & ~LookupFlags.Sensors;
|
||||
foreach (var e in _lookup.GetEntitiesInRange(targetPos, _lookupSize, flags: flags))
|
||||
{
|
||||
if (!entities.Contains(e))
|
||||
entities.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have to do FoV checks?
|
||||
if ((visibility & MenuVisibility.NoFov) == 0)
|
||||
{
|
||||
TryComp(player.Value, out ExaminerComponent? examiner);
|
||||
|
||||
foreach (var ent in entitiesUnderMouse)
|
||||
TryComp(player, out ExaminerComponent? examiner);
|
||||
for (var i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_examine.CanExamine(player.Value, targetPos, Predicate, ent.Uid, examiner))
|
||||
_entities.Add(ent.Uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var ent in entitiesUnderMouse)
|
||||
{
|
||||
_entities.Add(ent.Uid);
|
||||
if (!_examine.CanExamine(player, targetPos, e => e == player, entities[i], examiner))
|
||||
entities.RemoveSwap(i);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're in a container list all other entities in it.
|
||||
if (_containers.TryGetContainingContainer(player.Value, out var container))
|
||||
{
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
if (ent == player.Value || _entities.Contains(ent))
|
||||
continue;
|
||||
if ((visibility & MenuVisibility.Invisible) != 0)
|
||||
return entities.Count != 0;
|
||||
|
||||
if ((visibility & MenuVisibility.NoFov) == 0x0 || _examine.CanExamine(player.Value, targetPos, examined: ent))
|
||||
{
|
||||
_entities.Add(ent);
|
||||
}
|
||||
}
|
||||
for (var i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_tagSystem.HasTag(entities[i], "HideContextMenu"))
|
||||
entities.RemoveSwap(i);
|
||||
}
|
||||
|
||||
if (_entities.Count == 0)
|
||||
return false;
|
||||
// Unless we added entities in containers, every entity should already have a visible sprite due to
|
||||
// the fact that we used the sprite tree query.
|
||||
if (container == null && (visibility & MenuVisibility.InContainer) == 0)
|
||||
return entities.Count != 0;
|
||||
|
||||
if (visibility == MenuVisibility.All)
|
||||
var spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
for (var i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
result = new (_entities);
|
||||
return true;
|
||||
if (!spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible)
|
||||
entities.RemoveSwap(i);
|
||||
}
|
||||
|
||||
// remove any entities in containers
|
||||
if ((visibility & MenuVisibility.InContainer) == 0)
|
||||
{
|
||||
for (var i = _entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var entity = _entities[i];
|
||||
|
||||
if (ContainerSystem.IsInSameOrTransparentContainer(player.Value, entity))
|
||||
continue;
|
||||
|
||||
_entities.RemoveSwap(i);
|
||||
}
|
||||
}
|
||||
|
||||
// remove any invisible entities
|
||||
if ((visibility & MenuVisibility.Invisible) == 0)
|
||||
{
|
||||
var spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
for (var i = _entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var entity = _entities[i];
|
||||
|
||||
if (!spriteQuery.TryGetComponent(entity, out var spriteComponent) ||
|
||||
!spriteComponent.Visible ||
|
||||
_tagSystem.HasTag(entity, "HideContextMenu"))
|
||||
{
|
||||
_entities.RemoveSwap(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_entities.Count == 0)
|
||||
return false;
|
||||
|
||||
result = new(_entities);
|
||||
return true;
|
||||
return entities.Count != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user