diff --git a/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs index 3815ab27d..96aba2819 100644 --- a/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs +++ b/Assets/Scripts/Editor/DrawnPoseWiringVerify.cs @@ -100,6 +100,7 @@ namespace Aetheria.EditorTools { var settings = AssetDatabase.LoadAssetAtPath("Assets/Resources/Settings.asset")?.GameplaySettings ?? throw new InvalidOperationException("No Settings.asset."); + settings.FloaterLayerHeight = -1.5f; // nonzero, so a presenter that drops the floater layer fails var wired = SessionState.GetString(Wiring, "").Split('|').Select(entry => entry.Split('=')).ToDictionary(pair => pair[0], pair => pair[1]); var pickupPrefab = AssetDatabase.LoadAssetAtPath(wired["SimpleCommodityPickup"])?.GetComponent(); @@ -178,6 +179,8 @@ namespace Aetheria.EditorTools want = mine.Body.Position + mineVelocity * lead; Require(Mathf.Abs(seen.x - want.x) < Tolerance && Mathf.Abs(seen.z - want.y) < Tolerance, $"MineInstance at lead {lead}: drawn x/z {seen.x},{seen.z}, the step plus velocity times lead {want.x},{want.y}."); + Require(Mathf.Abs(seen.y - DrawAhead.FloaterHeight(zone, settings, new float2(want.x, want.y), lead)) < Tolerance, + $"MineInstance y {seen.y} is not on the floater layer."); // The round's line is a function of the drawn time: it moves RoundSpeed * lead along its direction between leads. seen = projectile.transform.position; diff --git a/Assets/Scripts/ServerShared/Behaviors/Pickup.cs b/Assets/Scripts/ServerShared/Behaviors/Pickup.cs index 0e4747183..29cd9c383 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Pickup.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Pickup.cs @@ -167,5 +167,5 @@ public class Pickup : Behavior, IAlwaysUpdatedBehavior } private void Publish(GrabPhase phase, FloatingItem item, float duration) => - Entity.Capabilities.PublishGrab(new GrabEvent(item.Id, phase, float3(item.Body.Position.x, Entity.Position.y, item.Body.Position.y), duration)); + Entity.Capabilities.PublishGrab(new GrabEvent(item.Id, phase, float3(item.Body.Position.x, DrawAhead.FloaterHeight(Entity.Zone, Entity.ItemManager.GameplaySettings, item.Body.Position, 0), item.Body.Position.y), duration)); } diff --git a/tests/Aetheria.Shared.Tests/ContactTests.cs b/tests/Aetheria.Shared.Tests/ContactTests.cs index 2efc5431c..bccd2a90d 100644 --- a/tests/Aetheria.Shared.Tests/ContactTests.cs +++ b/tests/Aetheria.Shared.Tests/ContactTests.cs @@ -98,9 +98,9 @@ public sealed class ContactTests : IDisposable var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true); _caches.Add(cache); cache.Upsert(new VerseGrammar { Revision = 1 }); - HullData Hull(string name, Shape shape, float mass) => new HullData + HullData Hull(string name, Shape shape, float mass, HullType type = HullType.Ship) => new HullData { - Name = name, HullType = HullType.Ship, Shape = shape, Durability = 1000000, Mass = mass, Armor = 0, + Name = name, HullType = type, Shape = shape, Durability = 1000000, Mass = mass, Armor = 0, Hardpoints = { new HardpointData { Type = HardpointType.Sensors, Position = new int2(0, 0), Shape = new Shape() } } }; cache.Upsert(Hull("Solid", Solid(5, 5), 1000)); @@ -109,6 +109,9 @@ public sealed class ContactTests : IDisposable cache.Upsert(Hull("Mid", Solid(5, 5), 3000)); cache.Upsert(Hull("Ell", Ell(), 1000)); cache.Upsert(Hull("Longinus", ShippedLonginus(), 1000)); + // The orbital hulls carry their real types, so a filter by runtime kind and one by hull type are told apart. + cache.Upsert(Hull("Station", Solid(5, 5), 1000000, HullType.Station)); + cache.Upsert(Hull("Turret", Solid(5, 5), 1000, HullType.Turret)); cache.FlushAsync().Wait(); var ledger = new ProvenanceLedger(); @@ -390,9 +393,9 @@ public sealed class ContactTests : IDisposable { var lab = Build(); var ship = lab.Add("Solid", float3(0, 0, 0), float2(10, 0)); - var station = lab.AddOrbital("Heavy", float3(10, 0, 0), float2(0, 0)); + var station = lab.AddOrbital("Station", float3(10, 0, 0), float2(0, 0)); // Behind the ship and faster than it, so it closes on the ship as the station does. - var turret = lab.AddOrbital("Solid", float3(-10, 0, 0), float2(30, 0)); + var turret = lab.AddOrbital("Turret", float3(-10, 0, 0), float2(30, 0)); lab.Step(); Assert.Empty(lab.Events); Assert.Equal(float2(10, 0), ship.Velocity); @@ -408,7 +411,7 @@ public sealed class ContactTests : IDisposable var lab = Build(.5f); var a = lab.Add("Solid", float3(0, 0, 0), float2(10, 0)); var b = lab.Add("Solid", float3(10, 0, 0), float2(-10, 0)); - lab.AddOrbital("Heavy", float3(5, 0, 0), float2(0, 0)); + lab.AddOrbital("Station", float3(5, 0, 0), float2(0, 0)); lab.Step(); var contact = Assert.Single(lab.Events); diff --git a/tests/Aetheria.Shared.Tests/PickupTests.cs b/tests/Aetheria.Shared.Tests/PickupTests.cs index d88ed5e53..78e1d478a 100644 --- a/tests/Aetheria.Shared.Tests/PickupTests.cs +++ b/tests/Aetheria.Shared.Tests/PickupTests.cs @@ -74,7 +74,8 @@ public sealed class PickupTests : IDisposable TargetDetectionInfoThreshold = .1f, TargetGearInfoThreshold = .8f, FiringArc = 170, CommitHorizon = .5f, SchematicCellSize = 1f, UnaidedAccuracy = .05f, UnaidedTracking = 10f, UnaidedPrecision = 2f, AgentMinHitProbability = 0f, - LootDropProbability = 0, LootDropVelocity = 25, FloatingItemLifetime = lifetime + LootDropProbability = 0, LootDropVelocity = 25, FloatingItemLifetime = lifetime, + ShipLayerHeight = 3f, FloaterLayerHeight = -1.5f }; var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true); _caches.Add(cache); @@ -244,6 +245,22 @@ public sealed class PickupTests : IDisposable } } + // The beam ends on the loot where the loot is drawn: the field plus the floater layer, not the grabbing ship's plane + // (ruling orbitals-above-floaters-below). + [Fact] + public void AGrabPointRidesTheFloaterLayerNotTheShips() + { + using var bag = new CompositeDisposable(); + var lab = Build(Beam()); + var ship = Spawn(lab, float3(0, 0, 0)); + var item = Drift(lab, 40, 0); + var events = Watch(ship, bag); + ship.TractorCommand = 1; + for (var tick = 1; tick <= 150; tick++) lab.Zone.Update(1 / 60f); + Assert.NotEmpty(events); + Assert.All(events, e => Assert.Equal(lab.Zone.GetHeight(e.TargetPosition.xz, lab.Zone.ExactTime) - 1.5f, e.TargetPosition.y, 3)); + } + [Fact] public void GrabEventsNameTheFloatingItem() {