Skip to content

Commit

Permalink
Entity menu lookup changes (#32395)
Browse files Browse the repository at this point in the history
  • Loading branch information
ElectroJr authored Sep 23, 2024
1 parent c4b8260 commit caf34be
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 79 deletions.
157 changes: 78 additions & 79 deletions Content.Client/Verbs/VerbSystem.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,6 +13,7 @@
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;
Expand All @@ -30,11 +31,10 @@ public sealed class VerbSystem : SharedVerbSystem
[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.
Expand All @@ -43,128 +43,127 @@ public sealed class VerbSystem : SharedVerbSystem

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;

// Do we have to do FoV checks?
if ((visibility & MenuVisibility.NoFov) == 0)
{
TryComp(player.Value, out ExaminerComponent? examiner);

foreach (var ent in entitiesUnderMouse)
{
if (_examine.CanExamine(player.Value, targetPos, Predicate, ent.Uid, examiner))
_entities.Add(ent.Uid);
}
}
else
// 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)
{
foreach (var ent in entitiesUnderMouse)
{
_entities.Add(ent.Uid);
}
entities.Add(ent.Uid);
}

// If we're in a container list all other entities in it.
if (_containers.TryGetContainingContainer(player.Value, out var container))
// E.g., allow players in lockers to examine / interact with other entities in the same locker
if (_containers.TryGetContainingContainer((player, null), out var container))
{
foreach (var ent in container.ContainedEntities)
// 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))
{
if (ent == player.Value || _entities.Contains(ent))
continue;

if ((visibility & MenuVisibility.NoFov) == 0x0 || _examine.CanExamine(player.Value, targetPos, examined: ent))
// 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)
{
_entities.Add(ent);
if (!entities.Contains(ent))
entities.Add(ent);
}
}
}

if (_entities.Count == 0)
return false;

if (visibility == MenuVisibility.All)
if ((visibility & MenuVisibility.InContainer) != 0)
{
result = new (_entities);
return true;
// 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);
}
}

// remove any entities in containers
if ((visibility & MenuVisibility.InContainer) == 0)
// Do we have to do FoV checks?
if ((visibility & MenuVisibility.NoFov) == 0)
{
for (var i = _entities.Count - 1; i >= 0; i--)
TryComp(player, out ExaminerComponent? examiner);
for (var i = entities.Count - 1; i >= 0; i--)
{
var entity = _entities[i];

if (ContainerSystem.IsInSameOrTransparentContainer(player.Value, entity))
continue;

_entities.RemoveSwap(i);
if (!_examine.CanExamine(player, targetPos, e => e == player, entities[i], examiner))
entities.RemoveSwap(i);
}
}

// remove any invisible entities
if ((visibility & MenuVisibility.Invisible) == 0)
if ((visibility & MenuVisibility.Invisible) != 0)
return entities.Count != 0;

for (var i = entities.Count - 1; i >= 0; i--)
{
var spriteQuery = GetEntityQuery<SpriteComponent>();
if (_tagSystem.HasTag(entities[i], "HideContextMenu"))
entities.RemoveSwap(i);
}

for (var i = _entities.Count - 1; i >= 0; i--)
{
var entity = _entities[i];
// 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 (!spriteQuery.TryGetComponent(entity, out var spriteComponent) ||
!spriteComponent.Visible ||
_tagSystem.HasTag(entity, "HideContextMenu"))
{
_entities.RemoveSwap(i);
}
}
var spriteQuery = GetEntityQuery<SpriteComponent>();
for (var i = entities.Count - 1; i >= 0; i--)
{
if (!spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible)
entities.RemoveSwap(i);
}

if (_entities.Count == 0)
return false;

result = new(_entities);
return true;
return entities.Count != 0;
}

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ public static readonly CVarDef<bool>
public static readonly CVarDef<bool> ContrabandExamine =
CVarDef.Create("game.contraband_examine", true, CVar.SERVER | CVar.REPLICATED);

/// <summary>
/// Size of the lookup area for adding entities to the context menu
/// </summary>
public static readonly CVarDef<float> GameEntityMenuLookup =
CVarDef.Create("game.entity_menu_lookup", 0.25f, CVar.CLIENTONLY | CVar.ARCHIVE);

/*
* Discord
*/
Expand Down

0 comments on commit caf34be

Please sign in to comment.