From bf2ae6294df0361693758f111713ccc2685441a1 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 5 Jan 2023 18:29:27 +1300 Subject: [PATCH] Make clickmap lookups use the sprite tree (#13275) Co-authored-by: metalgearsloth --- .../Clickable/ClickableComponent.cs | 4 +- Content.Client/DragDrop/DragDropSystem.cs | 3 +- Content.Client/Gameplay/GameplayStateBase.cs | 38 +++++++++---------- .../Outline/InteractionOutlineSystem.cs | 2 +- Content.Client/Verbs/VerbSystem.cs | 2 +- .../Weapons/Melee/MeleeWeaponSystem.cs | 4 +- .../Weapons/Ranged/Systems/TetherGunSystem.cs | 2 +- .../Tests/ClickableTest.cs | 2 +- 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Content.Client/Clickable/ClickableComponent.cs b/Content.Client/Clickable/ClickableComponent.cs index 080fa89b2b..44734ce92f 100644 --- a/Content.Client/Clickable/ClickableComponent.cs +++ b/Content.Client/Clickable/ClickableComponent.cs @@ -21,9 +21,9 @@ namespace Content.Client.Clickable /// The draw depth for the sprite that captured the click. /// /// True if the click worked, false otherwise. - public bool CheckClick(SpriteComponent sprite, EntityQuery xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom) + public bool CheckClick(SpriteComponent sprite, TransformComponent transform, EntityQuery xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom) { - if (!sprite.Visible || !xformQuery.TryGetComponent(sprite.Owner, out var transform)) + if (!sprite.Visible) { drawDepth = default; renderOrder = default; diff --git a/Content.Client/DragDrop/DragDropSystem.cs b/Content.Client/DragDrop/DragDropSystem.cs index 488fc87eb6..1e12d53ba5 100644 --- a/Content.Client/DragDrop/DragDropSystem.cs +++ b/Content.Client/DragDrop/DragDropSystem.cs @@ -20,6 +20,7 @@ using Robust.Shared.Input; using Robust.Shared.Input.Binding; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using System.Linq; using DrawDepth = Content.Shared.DrawDepth.DrawDepth; namespace Content.Client.DragDrop @@ -306,7 +307,7 @@ namespace Content.Client.DragDrop if (_stateManager.CurrentState is GameplayState screen) { - entities = screen.GetEntitiesUnderPosition(args.Coordinates); + entities = screen.GetClickableEntities(args.Coordinates).ToList(); } else { diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs index 727eb2b299..e1ca614f91 100644 --- a/Content.Client/Gameplay/GameplayStateBase.cs +++ b/Content.Client/Gameplay/GameplayStateBase.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Content.Client.Clickable; using Content.Client.ContextMenu.UI; +using Robust.Client.ComponentTrees; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; @@ -49,7 +50,7 @@ namespace Content.Client.Gameplay EntityUid? uid = null; if (UserInterfaceManager.CurrentlyHovered is IViewportControl vp && _inputManager.MouseScreenPosition.IsValid) - uid = GetEntityUnderPosition(vp.ScreenToMap(_inputManager.MouseScreenPosition.Position)); + uid = GetClickedEntity(vp.ScreenToMap(_inputManager.MouseScreenPosition.Position)); else if (UserInterfaceManager.CurrentlyHovered is EntityMenuElement element) uid = element.Entity; @@ -74,48 +75,47 @@ namespace Content.Client.Gameplay _inputManager.KeyBindStateChanged -= OnKeyBindStateChanged; } - public EntityUid? GetEntityUnderPosition(MapCoordinates coordinates) + public EntityUid? GetClickedEntity(MapCoordinates coordinates) { - var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); - return entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null; + var first = GetClickableEntities(coordinates).FirstOrDefault(); + return first.IsValid() ? first : null; } - public IList GetEntitiesUnderPosition(EntityCoordinates coordinates) + public IEnumerable GetClickableEntities(EntityCoordinates coordinates) { - return GetEntitiesUnderPosition(coordinates.ToMap(_entityManager)); + return GetClickableEntities(coordinates.ToMap(_entityManager)); } - public IList GetEntitiesUnderPosition(MapCoordinates coordinates) + public IEnumerable GetClickableEntities(MapCoordinates coordinates) { // Find all the entities intersecting our click - var entities = _entityManager.EntitySysManager.GetEntitySystem().GetEntitiesIntersecting(coordinates.MapId, - Box2.CenteredAround(coordinates.Position, (1, 1)), LookupFlags.Uncontained | LookupFlags.Approximate); + var spriteTree = _entityManager.EntitySysManager.GetEntitySystem(); + var entities = spriteTree.QueryAabb(coordinates.MapId, Box2.CenteredAround(coordinates.Position, (1, 1)), true); // Check the entities against whether or not we can click them - var foundEntities = new List<(EntityUid clicked, int drawDepth, uint renderOrder, float bottom)>(); + var foundEntities = new List<(EntityUid, int, uint, float)>(entities.Count); var clickQuery = _entityManager.GetEntityQuery(); - var metaQuery = _entityManager.GetEntityQuery(); - var spriteQuery = _entityManager.GetEntityQuery(); var xformQuery = _entityManager.GetEntityQuery(); + // TODO: Smelly var eye = _eyeManager.CurrentEye; foreach (var entity in entities) { - if (clickQuery.TryGetComponent(entity, out var component) && - spriteQuery.TryGetComponent(entity, out var sprite) && - component.CheckClick(sprite, xformQuery, coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom)) + if (clickQuery.TryGetComponent(entity.Uid, out var component) && + component.CheckClick(entity.Component, entity.Transform, xformQuery, coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom)) { - foundEntities.Add((entity, drawDepthClicked, renderOrder, bottom)); + foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom)); } } if (foundEntities.Count == 0) return Array.Empty(); + // Do drawdepth & y-sorting. First index is the top-most sprite (opposite of normal render order). foundEntities.Sort(_comparer); - // 0 is the top element. - return foundEntities.Select(a => a.clicked).ToList(); + + return foundEntities.Select(a => a.Item1); } private sealed class ClickableEntityComparer : IComparer<(EntityUid clicked, int depth, uint renderOrder, float bottom)> @@ -166,7 +166,7 @@ namespace Content.Client.Gameplay if (args.Viewport is IViewportControl vp) { var mousePosWorld = vp.ScreenToMap(kArgs.PointerLocation.Position); - entityToClick = GetEntityUnderPosition(mousePosWorld); + entityToClick = GetClickedEntity(mousePosWorld); coordinates = _mapManager.TryFindGridAt(mousePosWorld, out var grid) ? grid.MapToGrid(mousePosWorld) : EntityCoordinates.FromMap(_mapManager, mousePosWorld); diff --git a/Content.Client/Outline/InteractionOutlineSystem.cs b/Content.Client/Outline/InteractionOutlineSystem.cs index ec5f4c6e46..26128fef50 100644 --- a/Content.Client/Outline/InteractionOutlineSystem.cs +++ b/Content.Client/Outline/InteractionOutlineSystem.cs @@ -118,7 +118,7 @@ public sealed class InteractionOutlineSystem : EntitySystem && _inputManager.MouseScreenPosition.IsValid) { var mousePosWorld = vp.ScreenToMap(_inputManager.MouseScreenPosition.Position); - entityToClick = screen.GetEntityUnderPosition(mousePosWorld); + entityToClick = screen.GetClickedEntity(mousePosWorld); if (vp is ScalingViewport svp) { diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index f62f13d37a..8a0b69d774 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -109,7 +109,7 @@ namespace Content.Client.Verbs // Do we have to do FoV checks? if ((visibility & MenuVisibility.NoFov) == 0) { - var entitiesUnderMouse = gameScreenBase.GetEntitiesUnderPosition(targetPos); + var entitiesUnderMouse = gameScreenBase.GetClickableEntities(targetPos).ToHashSet(); bool Predicate(EntityUid e) => e == player || entitiesUnderMouse.Contains(e); // first check the general location. diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index f3ba6edaa2..dad14c3ac5 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -112,7 +112,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem if (_stateManager.CurrentState is GameplayStateBase screen) { - target = screen.GetEntityUnderPosition(mousePos); + target = screen.GetClickedEntity(mousePos); } EntityManager.RaisePredictiveEvent(new DisarmAttackEvent(target, coordinates)); @@ -191,7 +191,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // TODO: UI Refactor update I assume if (_stateManager.CurrentState is GameplayStateBase screen) { - target = screen.GetEntityUnderPosition(mousePos); + target = screen.GetClickedEntity(mousePos); } RaisePredictiveEvent(new LightAttackEvent(target, weapon.Owner, coordinates)); diff --git a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs index 9b56fdd8d7..482643076b 100644 --- a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs @@ -84,7 +84,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem if (gameState is GameplayState game) { - var uid = game.GetEntityUnderPosition(mousePos); + var uid = game.GetClickedEntity(mousePos); if (uid != null) StartDragging(uid.Value, mousePos); diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index 390771ce8f..dcaf06829e 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -81,7 +81,7 @@ namespace Content.IntegrationTests.Tests var pos = clientEntManager.GetComponent(entity).WorldPosition; var clickable = clientEntManager.GetComponent(entity); - hit = clickable.CheckClick(sprite, xformQuery, (clickPosX, clickPosY) + pos, eye, out _, out _, out _); + hit = clickable.CheckClick(sprite, xformQuery.GetComponent(entity), xformQuery, (clickPosX, clickPosY) + pos, eye, out _, out _, out _); }); await server.WaitPost(() =>