]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Entity menu lookup changes (#32395)
authorLeon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Mon, 23 Sep 2024 07:28:42 +0000 (19:28 +1200)
committerGitHub <noreply@github.com>
Mon, 23 Sep 2024 07:28:42 +0000 (17:28 +1000)
Content.Client/Verbs/VerbSystem.cs
Content.Shared/CCVar/CCVars.cs

index f84389195f8c744531629e060c95c80c932a3225..f592303d2819804f140beb693826f5839bdcda38 100644 (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;
-
-            // 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>
index d6d8bafa0e6c1924e9c85c4223a6919b0571ae87..26101c7537ee4dfc7efc44596753443aa8cef013 100644 (file)
@@ -430,6 +430,12 @@ namespace Content.Shared.CCVar
         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
          */