Fix click sorting (#11657)
This commit is contained in:
@@ -9,8 +9,6 @@ namespace Content.Client.Clickable
|
|||||||
public sealed class ClickableComponent : Component
|
public sealed class ClickableComponent : Component
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
|
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
[DataField("bounds")] public DirBoundData? Bounds;
|
[DataField("bounds")] public DirBoundData? Bounds;
|
||||||
|
|
||||||
@@ -23,30 +21,31 @@ namespace Content.Client.Clickable
|
|||||||
/// The draw depth for the sprite that captured the click.
|
/// The draw depth for the sprite that captured the click.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>True if the click worked, false otherwise.</returns>
|
/// <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;
|
drawDepth = default;
|
||||||
renderOrder = default;
|
renderOrder = default;
|
||||||
|
bottom = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawDepth = sprite.DrawDepth;
|
drawDepth = sprite.DrawDepth;
|
||||||
renderOrder = sprite.RenderOrder;
|
renderOrder = sprite.RenderOrder;
|
||||||
|
var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
|
||||||
var transform = _entMan.GetComponent<TransformComponent>(Owner);
|
var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye);
|
||||||
var worldRot = transform.WorldRotation;
|
bottom = spriteBB.CalcBoundingBox().Bottom;
|
||||||
|
|
||||||
var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
|
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.
|
// 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;
|
Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
|
||||||
|
|
||||||
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
|
// 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));
|
var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos));
|
||||||
|
|
||||||
// Check explicitly defined click-able bounds
|
// 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
|
// 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;
|
continue;
|
||||||
|
|
||||||
var dir = SpriteComponent.Layer.GetDirection(rsiState.Directions, relativeRotation);
|
var dir = Layer.GetDirection(rsiState.Directions, relativeRotation);
|
||||||
|
|
||||||
// convert to layer-local coordinates
|
// convert to layer-local coordinates
|
||||||
layer.GetLayerDrawMatrix(dir, out var matrix);
|
layer.GetLayerDrawMatrix(dir, out var matrix);
|
||||||
@@ -95,6 +94,7 @@ namespace Content.Client.Clickable
|
|||||||
|
|
||||||
drawDepth = default;
|
drawDepth = default;
|
||||||
renderOrder = default;
|
renderOrder = default;
|
||||||
|
bottom = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using Content.Client.Clickable;
|
using Content.Client.Clickable;
|
||||||
using Content.Client.ContextMenu.UI;
|
using Content.Client.ContextMenu.UI;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
@@ -22,6 +23,7 @@ namespace Content.Client.Gameplay
|
|||||||
[Virtual]
|
[Virtual]
|
||||||
public class GameplayStateBase : State, IEntityEventSubscriber
|
public class GameplayStateBase : State, IEntityEventSubscriber
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||||
@@ -63,7 +65,7 @@ namespace Content.Client.Gameplay
|
|||||||
{
|
{
|
||||||
_vvm.RegisterDomain("enthover", ResolveVVHoverObject, ListVVHoverPaths);
|
_vvm.RegisterDomain("enthover", ResolveVVHoverObject, ListVVHoverPaths);
|
||||||
_inputManager.KeyBindStateChanged += OnKeyBindStateChanged;
|
_inputManager.KeyBindStateChanged += OnKeyBindStateChanged;
|
||||||
_comparer = new ClickableEntityComparer(_entityManager);
|
_comparer = new ClickableEntityComparer();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Shutdown()
|
protected override void Shutdown()
|
||||||
@@ -90,13 +92,21 @@ namespace Content.Client.Gameplay
|
|||||||
Box2.CenteredAround(coordinates.Position, (1, 1)), LookupFlags.Uncontained | LookupFlags.Approximate);
|
Box2.CenteredAround(coordinates.Position, (1, 1)), LookupFlags.Uncontained | LookupFlags.Approximate);
|
||||||
|
|
||||||
// Check the entities against whether or not we can click them
|
// 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)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
if (_entityManager.TryGetComponent<ClickableComponent?>(entity, out var component)
|
if (clickQuery.TryGetComponent(entity, out var component) &&
|
||||||
&& component.CheckClick(coordinates.Position, out var drawDepthClicked, out var renderOrder))
|
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);
|
foundEntities.Sort(_comparer);
|
||||||
// 0 is the top element.
|
// 0 is the top element.
|
||||||
foundEntities.Reverse();
|
|
||||||
return foundEntities.Select(a => a.clicked).ToList();
|
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 int Compare((EntityUid clicked, int depth, uint renderOrder, float bottom) x,
|
||||||
|
(EntityUid clicked, int depth, uint renderOrder, float bottom) y)
|
||||||
public ClickableEntityComparer(IEntityManager entities)
|
|
||||||
{
|
{
|
||||||
_entities = entities;
|
var cmp = y.depth.CompareTo(x.depth);
|
||||||
}
|
if (cmp != 0)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return val;
|
return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turning this off it can make picking stuff out of lockers and such up a bit annoying.
|
cmp = y.renderOrder.CompareTo(x.renderOrder);
|
||||||
/*
|
|
||||||
val = x.renderOrder.CompareTo(y.renderOrder);
|
|
||||||
if (val != 0)
|
|
||||||
{
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
var transX = _entities.GetComponent<TransformComponent>(x.clicked);
|
if (cmp != 0)
|
||||||
var transY = _entities.GetComponent<TransformComponent>(y.clicked);
|
|
||||||
val = transX.Coordinates.Y.CompareTo(transY.Coordinates.Y);
|
|
||||||
if (val != 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Content.Client.Clickable;
|
using Content.Client.Clickable;
|
||||||
|
using Content.Client.Gameplay;
|
||||||
using Content.Shared.Weapons.Ranged.Systems;
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.State;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
@@ -79,27 +81,23 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
|||||||
|
|
||||||
if (_dragging == null)
|
if (_dragging == null)
|
||||||
{
|
{
|
||||||
var bodyQuery = GetEntityQuery<PhysicsComponent>();
|
var gameState = IoCManager.Resolve<IStateManager>().CurrentState;
|
||||||
var lowest = new List<(int DrawDepth, uint RenderOrder, EntityUid Entity)>();
|
|
||||||
|
|
||||||
foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Static))
|
if (gameState is GameplayState game)
|
||||||
{
|
{
|
||||||
if (!bodyQuery.HasComponent(ent) ||
|
EntityUid? uid;
|
||||||
!TryComp<ClickableComponent>(ent, out var clickable) ||
|
|
||||||
!clickable.CheckClick(mousePos.Position, out var drawDepth, out var renderOrder)) continue;
|
|
||||||
|
|
||||||
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));
|
if (_dragging == null)
|
||||||
|
return;
|
||||||
foreach (var ent in lowest)
|
|
||||||
{
|
|
||||||
StartDragging(ent.Entity, mousePos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dragging == null) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp<TransformComponent>(_dragging!.Value, out var xform) ||
|
if (!TryComp<TransformComponent>(_dragging!.Value, out var xform) ||
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var clientEntManager = client.ResolveDependency<IEntityManager>();
|
var clientEntManager = client.ResolveDependency<IEntityManager>();
|
||||||
var serverEntManager = server.ResolveDependency<IEntityManager>();
|
var serverEntManager = server.ResolveDependency<IEntityManager>();
|
||||||
var eyeManager = client.ResolveDependency<IEyeManager>();
|
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);
|
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
@@ -69,7 +72,8 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
await client.WaitPost(() =>
|
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
|
// these tests currently all assume player eye is 0
|
||||||
eyeManager.CurrentEye.Rotation = 0;
|
eyeManager.CurrentEye.Rotation = 0;
|
||||||
@@ -77,7 +81,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
var pos = clientEntManager.GetComponent<TransformComponent>(entity).WorldPosition;
|
var pos = clientEntManager.GetComponent<TransformComponent>(entity).WorldPosition;
|
||||||
var clickable = clientEntManager.GetComponent<ClickableComponent>(entity);
|
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(() =>
|
await server.WaitPost(() =>
|
||||||
|
|||||||
Reference in New Issue
Block a user