Entity menu lookup changes (#32395)

This commit is contained in:
Leon Friedrich
2024-09-23 19:28:42 +12:00
committed by GitHub
parent c4b8260f23
commit caf34be616
2 changed files with 93 additions and 88 deletions

View File

@@ -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>