diff --git a/Robust.UnitTesting/Client/Sprite/SpriteBoundsTest.cs b/Robust.UnitTesting/Client/Sprite/SpriteBoundsTest.cs new file mode 100644 index 000000000..f7bb1cfd7 --- /dev/null +++ b/Robust.UnitTesting/Client/Sprite/SpriteBoundsTest.cs @@ -0,0 +1,132 @@ +using System.Numerics; +using System.Threading.Tasks; +using NUnit.Framework; +using Robust.Client; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; + +namespace Robust.UnitTesting.Client.Sprite; + +public sealed class SpriteBoundsTest : RobustIntegrationTest +{ + private static readonly string Prototypes = @" +- type: entity + id: spriteBoundsTest1 + components: + - type: Sprite + sprite: debugRotation.rsi + layers: + - state: direction1 + +- type: entity + id: spriteBoundsTest2 + components: + - type: Sprite + sprite: debugRotation.rsi + layers: + - state: direction1 + - state: direction1 + rotation: 0.1 + +- type: entity + id: spriteBoundsTest3 + components: + - type: Sprite + sprite: debugRotation.rsi + layers: + - state: direction1 + - state: direction1 + rotation: 0.1 + visible: false +"; + + [Test] + public async Task TestSpriteBounds() + { + var client = StartClient(new ClientIntegrationOptions {ExtraPrototypes = Prototypes}); + await client.WaitIdleAsync(); + var baseClient = client.Resolve(); + + await client.WaitPost(() => baseClient.StartSinglePlayer()); + await client.WaitIdleAsync(); + + var entMan = client.EntMan; + var sys = client.System(); + + EntityUid uid1 = default; + EntityUid uid2 = default; + EntityUid uid3 = default; + + await client.WaitPost(() => + { + uid1 = entMan.Spawn("spriteBoundsTest1"); + uid2 = entMan.Spawn("spriteBoundsTest2"); + uid3 = entMan.Spawn("spriteBoundsTest3"); + }); + + var ent1 = new Entity(uid1, entMan.GetComponent(uid1)); + var ent2 = new Entity(uid2, entMan.GetComponent(uid2)); + var ent3 = new Entity(uid3, entMan.GetComponent(uid3)); + + // None of the entities have empty bounding boxes + var box1 = sys.GetLocalBounds(ent1); + var box2 = sys.GetLocalBounds(ent2); + var box3 = sys.GetLocalBounds(ent3); + Assert.That(!box1.EqualsApprox(Box2.Empty)); + Assert.That(!box2.EqualsApprox(Box2.Empty)); + Assert.That(!box3.EqualsApprox(Box2.Empty)); + + // ent2 should have a larger bb than ent1 as it has a visible rotated layer + // ents 1 & 3 should have the same bounds, as the rotated layer is invisible in ent3 + Assert.That(box1.EqualsApprox(box3)); + Assert.That(!box1.EqualsApprox(box2)); + Assert.That(box2.EqualsApprox(ent2.Comp.Layers[1].Bounds)); + Assert.That(box2.Size.X, Is.GreaterThan(box1.Size.X)); + Assert.That(box2.Size.Y, Is.GreaterThan(box1.Size.Y)); + + // Toggling layer visibility updates the bounds + sys.LayerSetVisible(ent2!, 1, false); + sys.LayerSetVisible(ent3!, 1, true); + + var newBox2 = sys.GetLocalBounds(ent2); + var newBox3 = sys.GetLocalBounds(ent3); + Assert.That(!newBox2.EqualsApprox(Box2.Empty)); + Assert.That(!newBox3.EqualsApprox(Box2.Empty)); + + Assert.That(box1.EqualsApprox(newBox2)); + Assert.That(!box1.EqualsApprox(newBox3)); + Assert.That(newBox3.EqualsApprox(ent3.Comp.Layers[1].Bounds)); + Assert.That(newBox3.Size.X, Is.GreaterThan(box1.Size.X)); + Assert.That(newBox3.Size.Y, Is.GreaterThan(box1.Size.Y)); + + // Changing the rotation, offset, scale, all trigger a bounds updatge + sys.LayerSetRotation(ent3!, 1, Angle.Zero); + var box = sys.GetLocalBounds(ent3); + Assert.That(box1.EqualsApprox(box)); + + // scale + sys.LayerSetScale(ent3!, 1, Vector2.One * 2); + box = sys.GetLocalBounds(ent3); + Assert.That(box1.EqualsApprox(box.Scale(0.5f))); + sys.LayerSetScale(ent3!, 1, Vector2.One); + box = sys.GetLocalBounds(ent3); + Assert.That(box1.EqualsApprox(box)); + + // offset + Assert.That(box.Center, Is.Approximately(Vector2.Zero)); + sys.LayerSetOffset(ent3!, 1, Vector2.One); + box = sys.GetLocalBounds(ent3); + Assert.That(box.Size.X, Is.GreaterThan(box1.Size.X)); + Assert.That(box.Size.Y, Is.GreaterThan(box1.Size.Y)); + Assert.That(box.Center.X, Is.GreaterThan(0)); + Assert.That(box.Center.Y, Is.GreaterThan(0)); + + await client.WaitPost(() => + { + entMan.DeleteEntity(uid1); + entMan.DeleteEntity(uid2); + entMan.DeleteEntity(uid3); + }); + } +}