Fix click sorting (#11657)
This commit is contained in:
@@ -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.
|
||||
/// </param>
|
||||
/// <returns>True if the click worked, false otherwise.</returns>
|
||||
public bool CheckClick(Vector2 worldPos, out int drawDepth, out uint renderOrder)
|
||||
public bool CheckClick(SpriteComponent sprite, EntityQuery<TransformComponent> 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<TransformComponent>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ClickableComponent>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
var spriteQuery = _entityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
// TODO: Smelly
|
||||
var eye = _eyeManager.CurrentEye;
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (_entityManager.TryGetComponent<ClickableComponent?>(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;
|
||||
var cmp = y.depth.CompareTo(x.depth);
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
|
||||
public int Compare((EntityUid clicked, int depth, uint renderOrder) x,
|
||||
(EntityUid clicked, int depth, uint renderOrder) y)
|
||||
cmp = y.renderOrder.CompareTo(x.renderOrder);
|
||||
|
||||
if (cmp != 0)
|
||||
{
|
||||
var val = x.depth.CompareTo(y.depth);
|
||||
if (val != 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.bottom.CompareTo(x.bottom);
|
||||
|
||||
var transX = _entities.GetComponent<TransformComponent>(x.clicked);
|
||||
var transY = _entities.GetComponent<TransformComponent>(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);
|
||||
return y.clicked.CompareTo(x.clicked);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<PhysicsComponent>();
|
||||
var lowest = new List<(int DrawDepth, uint RenderOrder, EntityUid Entity)>();
|
||||
var gameState = IoCManager.Resolve<IStateManager>().CurrentState;
|
||||
|
||||
if (gameState is GameplayState game)
|
||||
{
|
||||
EntityUid? uid;
|
||||
|
||||
foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Static))
|
||||
{
|
||||
if (!bodyQuery.HasComponent(ent) ||
|
||||
!TryComp<ClickableComponent>(ent, out var clickable) ||
|
||||
!clickable.CheckClick(mousePos.Position, out var drawDepth, out var renderOrder)) continue;
|
||||
uid = game.GetEntityUnderPosition(mousePos);
|
||||
|
||||
lowest.Add((drawDepth, renderOrder, ent));
|
||||
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<TransformComponent>(_dragging!.Value, out var xform) ||
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace Content.IntegrationTests.Tests
|
||||
var clientEntManager = client.ResolveDependency<IEntityManager>();
|
||||
var serverEntManager = server.ResolveDependency<IEntityManager>();
|
||||
var eyeManager = client.ResolveDependency<IEyeManager>();
|
||||
var spriteQuery = clientEntManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = clientEntManager.GetEntityQuery<TransformComponent>();
|
||||
var eye = client.ResolveDependency<IEyeManager>().CurrentEye;
|
||||
|
||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||
await server.WaitPost(() =>
|
||||
@@ -69,7 +72,8 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
await client.WaitPost(() =>
|
||||
{
|
||||
clientEntManager.GetComponent<SpriteComponent>(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<TransformComponent>(entity).WorldPosition;
|
||||
var clickable = clientEntManager.GetComponent<ClickableComponent>(entity);
|
||||
|
||||
hit = clickable.CheckClick((clickPosX, clickPosY) + pos, out _, out _);
|
||||
hit = clickable.CheckClick(sprite, xformQuery, (clickPosX, clickPosY) + pos, eye, out _, out _, out _);
|
||||
});
|
||||
|
||||
await server.WaitPost(() =>
|
||||
|
||||
Reference in New Issue
Block a user