diff --git a/tests/Aetheria.Shared.Tests/ContactRevealTests.cs b/tests/Aetheria.Shared.Tests/ContactRevealTests.cs index 9e9c4fcca..29c5377c2 100644 --- a/tests/Aetheria.Shared.Tests/ContactRevealTests.cs +++ b/tests/Aetheria.Shared.Tests/ContactRevealTests.cs @@ -59,6 +59,16 @@ public sealed partial class ContactRevealTests : IDisposable // PingRange (300) over the intercept factor. cache.Upsert(Eye("Shout", sensitivity: 2f, interceptFactor: 2f, beamWidth: 10f)); cache.Upsert(Eye("Roar", sensitivity: 2f, interceptFactor: .5f, beamWidth: 10f)); + // The catalog's sensor alpha (AetherDb sensor-stat-set): .5 to 2 over one Quality term on the equipped lot's + // emission-discipline role, exponent log2(3) so a middling part (.5) reads 1. + var disciplined = Eye("Disciplined", sensitivity: 2f); + disciplined.Roles.Add(new ItemRole { Name = "emission-discipline" }); + disciplined.Behaviors.OfType().Single().InterceptFactor = new PerformanceStat + { + Min = .5f, Max = 2f, + Terms = { new StatTerm { Source = StatSource.Quality, Exponent = MathF.Log(3f) / MathF.Log(2f), Role = "emission-discipline" } } + }; + cache.Upsert(disciplined); cache.Upsert(Listener("Listener", 3f)); cache.Upsert(new ConsumableItemData { Name = "Earpiece", Duration = 30f, Shape = new Shape(), Behaviors = { new ListenerData { Multiple = Const(3f) } } }); cache.Upsert(Listener("Wide", 5f)); diff --git a/tests/Aetheria.Shared.Tests/EmissionTests.cs b/tests/Aetheria.Shared.Tests/EmissionTests.cs index 1a721172a..afcb33ddc 100644 --- a/tests/Aetheria.Shared.Tests/EmissionTests.cs +++ b/tests/Aetheria.Shared.Tests/EmissionTests.cs @@ -314,4 +314,23 @@ public sealed partial class ContactRevealTests Assert.Equal(1f, locker.Equipment.SelectMany(i => i.Behaviors).OfType().Single().InterceptFactor); Assert.Equal(4f, hushed.Equipment.SelectMany(i => i.Behaviors).OfType().Single().InterceptFactor); } + + // A catalog-style sensor reads its intercept factor through the lot the ship equips it from: the lot's + // emission-discipline fill is the input (the workmanship is a decoy at .123), so a Sensor that read only the quiet or only + // the loud end, or the workmanship, reads the wrong number. The expectations are lerp(.5, 2, q^log2(3)): a middling part + // reads 1, the Lucent mean (.2) reads about .62 and the Finch mean (.8) about 1.55. + [Theory] + [InlineData(.5f, 1f)] + [InlineData(.2f, .6168f)] + [InlineData(.8f, 1.5534f)] + public void ASensorReadsItsInterceptFactorFromItsLotsEmissionDiscipline(float discipline, float expected) + { + var lot = _items.CreateLot(_openCache.GetByName("Disciplined"), _maker, .123f); + _items.Lots[lot].Roles = new List { new RoleFill { Role = "emission-discipline", Quality = discipline } }; + var ship = Add(float3(0, 0, 0), Array.Empty()); + Assert.True(ship.TryEquip(new EquippableItem { Data = _items.ItemData.RefOf(_items.ItemData.GetByName("Disciplined")), Durability = 10, Lot = lot }, new int2(0, 0))); + _zone.Update(0f); + + Assert.Equal(expected, SensorOf(ship).InterceptFactor, 3); + } } diff --git a/tests/Aetheria.Shared.Tests/EvasionTermTests.cs b/tests/Aetheria.Shared.Tests/EvasionTermTests.cs index e3cb9d039..a02755614 100644 --- a/tests/Aetheria.Shared.Tests/EvasionTermTests.cs +++ b/tests/Aetheria.Shared.Tests/EvasionTermTests.cs @@ -296,7 +296,10 @@ public sealed partial class RunStartTests return (Half(float2(1, 0)), Half(float2(0, 1))); } - private const float DjinniNoseOnFactor = 1.75f, DjinniBroadsideFactor = 1.36f; + // Each factor is fitted to its own ship's fleet fit: reach over measured motion. Djinni: 1.69 nose-on and 1.31 broadside at + // the last measurement, each with about 4% to spare. Longinus broadside: 1.23 (reach 12.74 over measured 10.40) with + // about 3.5% to spare, so an inflation of its envelope of 4% or more breaks its half. + private const float DjinniNoseOnFactor = 1.75f, DjinniBroadsideFactor = 1.36f, LonginusBroadsideFactor = 1.27f; [Fact] public void ReachIsWhatTheShipCanDo() @@ -342,7 +345,7 @@ public sealed partial class RunStartTests // with other rolls). The ceiling is that, from the columns // the allocator solves with, not an open bound: an envelope inflated by a factor (doubled: 2.7 and 2.3) breaks it. Assert.InRange(reach.noseOn, measured.noseOn * .9f, allocated.noseOn * 1.5f); - Assert.InRange(reach.broadside, measured.broadside * .9f, measured.broadside * DjinniBroadsideFactor); + Assert.InRange(reach.broadside, measured.broadside * .9f, measured.broadside * LonginusBroadsideFactor); Assert.InRange(measured.noseOn, allocated.noseOn * .9f, allocated.noseOn * 1.15f); Assert.InRange(measured.broadside, allocated.broadside * .85f, allocated.broadside * 1.15f); }