From b6a59051ddf8c26be634862d01f851ef69acfc4c Mon Sep 17 00:00:00 2001
From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Date: Mon, 19 Dec 2022 06:41:04 +1100
Subject: [PATCH] Fix click sorting (#11657)
---
.../Clickable/ClickableComponent.cs | 22 +++----
Content.Client/Gameplay/GameplayStateBase.cs | 65 +++++++++----------
.../Weapons/Ranged/Systems/TetherGunSystem.cs | 30 ++++-----
.../Tests/ClickableTest.cs | 8 ++-
4 files changed, 63 insertions(+), 62 deletions(-)
diff --git a/Content.Client/Clickable/ClickableComponent.cs b/Content.Client/Clickable/ClickableComponent.cs
index 231972e0a7..2c09798c1c 100644
--- a/Content.Client/Clickable/ClickableComponent.cs
+++ b/Content.Client/Clickable/ClickableComponent.cs
@@ -9,8 +9,6 @@ namespace Content.Client.Clickable
public sealed class ClickableComponent : Component
{
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
- [Dependency] private readonly IEyeManager _eyeManager = default!;
- [Dependency] private readonly IEntityManager _entMan = default!;
[DataField("bounds")] public DirBoundData? Bounds;
@@ -23,30 +21,31 @@ namespace Content.Client.Clickable
/// The draw depth for the sprite that captured the click.
///
/// True if the click worked, false otherwise.
- public bool CheckClick(Vector2 worldPos, out int drawDepth, out uint renderOrder)
+ public bool CheckClick(SpriteComponent sprite, EntityQuery xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
{
- if (!_entMan.TryGetComponent(Owner, out SpriteComponent? sprite) || !sprite.Visible)
+ if (!sprite.Visible || !xformQuery.TryGetComponent(sprite.Owner, out var transform))
{
drawDepth = default;
renderOrder = default;
+ bottom = default;
return false;
}
drawDepth = sprite.DrawDepth;
renderOrder = sprite.RenderOrder;
-
- var transform = _entMan.GetComponent(Owner);
- var worldRot = transform.WorldRotation;
+ var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
+ var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye);
+ bottom = spriteBB.CalcBoundingBox().Bottom;
var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
- var relativeRotation = (worldRot + _eyeManager.CurrentEye.Rotation).Reduced().FlipPositive();
+ var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
- var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -_eyeManager.CurrentEye.Rotation : worldRot - cardinalSnapping);
+ var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos));
// Check explicitly defined click-able bounds
@@ -70,10 +69,10 @@ namespace Content.Client.Clickable
}
// Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
- if (layer.State == null || layer.ActualRsi is not RSI rsi || !rsi.TryGetState(layer.State, out var rsiState))
+ if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState))
continue;
- var dir = SpriteComponent.Layer.GetDirection(rsiState.Directions, relativeRotation);
+ var dir = Layer.GetDirection(rsiState.Directions, relativeRotation);
// convert to layer-local coordinates
layer.GetLayerDrawMatrix(dir, out var matrix);
@@ -95,6 +94,7 @@ namespace Content.Client.Clickable
drawDepth = default;
renderOrder = default;
+ bottom = default;
return false;
}
diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs
index cb65ece0fc..727eb2b299 100644
--- a/Content.Client/Gameplay/GameplayStateBase.cs
+++ b/Content.Client/Gameplay/GameplayStateBase.cs
@@ -4,6 +4,7 @@ using System.Linq;
using Content.Client.Clickable;
using Content.Client.ContextMenu.UI;
using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.State;
@@ -22,6 +23,7 @@ namespace Content.Client.Gameplay
[Virtual]
public class GameplayStateBase : State, IEntityEventSubscriber
{
+ [Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
@@ -63,7 +65,7 @@ namespace Content.Client.Gameplay
{
_vvm.RegisterDomain("enthover", ResolveVVHoverObject, ListVVHoverPaths);
_inputManager.KeyBindStateChanged += OnKeyBindStateChanged;
- _comparer = new ClickableEntityComparer(_entityManager);
+ _comparer = new ClickableEntityComparer();
}
protected override void Shutdown()
@@ -90,13 +92,21 @@ namespace Content.Client.Gameplay
Box2.CenteredAround(coordinates.Position, (1, 1)), LookupFlags.Uncontained | LookupFlags.Approximate);
// Check the entities against whether or not we can click them
- var foundEntities = new List<(EntityUid clicked, int drawDepth, uint renderOrder)>();
+ var foundEntities = new List<(EntityUid clicked, int drawDepth, uint renderOrder, float bottom)>();
+ 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 (_entityManager.TryGetComponent(entity, out var component)
- && component.CheckClick(coordinates.Position, out var drawDepthClicked, out var renderOrder))
+ 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))
{
- foundEntities.Add((entity, drawDepthClicked, renderOrder));
+ foundEntities.Add((entity, drawDepthClicked, renderOrder, bottom));
}
}
@@ -105,46 +115,35 @@ namespace Content.Client.Gameplay
foundEntities.Sort(_comparer);
// 0 is the top element.
- foundEntities.Reverse();
return foundEntities.Select(a => a.clicked).ToList();
}
- private sealed class ClickableEntityComparer : IComparer<(EntityUid clicked, int depth, uint renderOrder)>
+ private sealed class ClickableEntityComparer : IComparer<(EntityUid clicked, int depth, uint renderOrder, float bottom)>
{
- private readonly IEntityManager _entities;
-
- public ClickableEntityComparer(IEntityManager entities)
+ public int Compare((EntityUid clicked, int depth, uint renderOrder, float bottom) x,
+ (EntityUid clicked, int depth, uint renderOrder, float bottom) y)
{
- _entities = entities;
- }
-
- public int Compare((EntityUid clicked, int depth, uint renderOrder) x,
- (EntityUid clicked, int depth, uint renderOrder) y)
- {
- var val = x.depth.CompareTo(y.depth);
- if (val != 0)
+ var cmp = y.depth.CompareTo(x.depth);
+ if (cmp != 0)
{
- return val;
+ return cmp;
}
- // Turning this off it can make picking stuff out of lockers and such up a bit annoying.
- /*
- val = x.renderOrder.CompareTo(y.renderOrder);
- if (val != 0)
- {
- return val;
- }
- */
+ cmp = y.renderOrder.CompareTo(x.renderOrder);
- var transX = _entities.GetComponent(x.clicked);
- var transY = _entities.GetComponent(y.clicked);
- val = transX.Coordinates.Y.CompareTo(transY.Coordinates.Y);
- if (val != 0)
+ if (cmp != 0)
{
- return val;
+ return cmp;
}
- return x.clicked.CompareTo(y.clicked);
+ cmp = y.bottom.CompareTo(x.bottom);
+
+ if (cmp != 0)
+ {
+ return cmp;
+ }
+
+ return y.clicked.CompareTo(x.clicked);
}
}
diff --git a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs
index 67e23930ca..9c3e214e19 100644
--- a/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs
+++ b/Content.Client/Weapons/Ranged/Systems/TetherGunSystem.cs
@@ -1,8 +1,10 @@
using Content.Client.Clickable;
+using Content.Client.Gameplay;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
+using Robust.Client.State;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Physics.Components;
@@ -79,27 +81,23 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
if (_dragging == null)
{
- var bodyQuery = GetEntityQuery();
- var lowest = new List<(int DrawDepth, uint RenderOrder, EntityUid Entity)>();
+ var gameState = IoCManager.Resolve().CurrentState;
- foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Static))
+ if (gameState is GameplayState game)
{
- if (!bodyQuery.HasComponent(ent) ||
- !TryComp(ent, out var clickable) ||
- !clickable.CheckClick(mousePos.Position, out var drawDepth, out var renderOrder)) continue;
+ EntityUid? uid;
- lowest.Add((drawDepth, renderOrder, ent));
+ foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Static))
+ {
+ uid = game.GetEntityUnderPosition(mousePos);
+
+ if (uid != null)
+ StartDragging(uid.Value, mousePos);
+ }
}
- lowest.Sort((x, y) => y.DrawDepth == x.DrawDepth ? y.RenderOrder.CompareTo(x.RenderOrder) : y.DrawDepth.CompareTo(x.DrawDepth));
-
- foreach (var ent in lowest)
- {
- StartDragging(ent.Entity, mousePos);
- break;
- }
-
- if (_dragging == null) return;
+ if (_dragging == null)
+ return;
}
if (!TryComp(_dragging!.Value, out var xform) ||
diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs
index 670f1313ea..390771ce8f 100644
--- a/Content.IntegrationTests/Tests/ClickableTest.cs
+++ b/Content.IntegrationTests/Tests/ClickableTest.cs
@@ -53,6 +53,9 @@ namespace Content.IntegrationTests.Tests
var clientEntManager = client.ResolveDependency();
var serverEntManager = server.ResolveDependency();
var eyeManager = client.ResolveDependency();
+ var spriteQuery = clientEntManager.GetEntityQuery();
+ var xformQuery = clientEntManager.GetEntityQuery();
+ var eye = client.ResolveDependency().CurrentEye;
var testMap = await PoolManager.CreateTestMap(pairTracker);
await server.WaitPost(() =>
@@ -69,7 +72,8 @@ namespace Content.IntegrationTests.Tests
await client.WaitPost(() =>
{
- clientEntManager.GetComponent(entity).Scale = (scale, scale);
+ var sprite = spriteQuery.GetComponent(entity);
+ sprite.Scale = (scale, scale);
// these tests currently all assume player eye is 0
eyeManager.CurrentEye.Rotation = 0;
@@ -77,7 +81,7 @@ namespace Content.IntegrationTests.Tests
var pos = clientEntManager.GetComponent(entity).WorldPosition;
var clickable = clientEntManager.GetComponent(entity);
- hit = clickable.CheckClick((clickPosX, clickPosY) + pos, out _, out _);
+ hit = clickable.CheckClick(sprite, xformQuery, (clickPosX, clickPosY) + pos, eye, out _, out _, out _);
});
await server.WaitPost(() =>