Fix click sorting (#11657)

This commit is contained in:
metalgearsloth
2022-12-19 06:41:04 +11:00
committed by GitHub
parent 8f352f87c2
commit b6a59051dd
4 changed files with 63 additions and 62 deletions

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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) ||

View File

@@ -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(() =>