From 0e9b10c3ae9bdb26cb026bf64c8a2d8d716a5d84 Mon Sep 17 00:00:00 2001 From: Nibu Date: Wed, 11 Nov 2026 05:03:57 +0000 Subject: [PATCH] Crossing: the crosser is launched at the speed it should cross at, since a hull no longer decays below top speed 845f6c3aa (aetheria-gravity:ruling:no-drag-below-top-speed) removed hull drag below a hull's top speed. The crosser was staged at the Djinni's 50 m/s cap on the premise that drag slowed it to ~25 m/s at the bow line; it now holds 50 m/s and crosses at 2.57 s, 50 m/s relative, 1.1 rad/s, outside the tuned encounter (3.6 s, 25 m/s, 0.56 rad/s) that the Brief, the pass window and CrossingHoldsAcrossTheStartWindow all describe. CrossingConditions and CrossingHoldsAcrossTheStartWindow failed on that stale tuning, not on a defect. The encounter is restaged to reproduce its tuning under the ruled physics: launched at 25 m/s from 90 m out (probed over speed x start; constant-speed passes are dx / v seconds, 45 m ahead). The pass lands at 3.63 s, 44.7 m, 24.9 m/s relative, 0.56 rad/s. The test's launch-speed assertions now pin the scenario's 25 m/s instead of "most of the cap"; the time, distance, relative-speed and angular-rate windows are unchanged. If drag below top speed returns, the crosser decays and the pass leaves the window. Co-Authored-By: Claude Sonnet 5.5 --- Assets/Scripts/ServerShared/Scenarios/Crossing.cs | 12 +++++++----- tests/Aetheria.Shared.Tests/CrossingScenarioTests.cs | 10 +++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Assets/Scripts/ServerShared/Scenarios/Crossing.cs b/Assets/Scripts/ServerShared/Scenarios/Crossing.cs index 61529d6d9..e77f50166 100644 --- a/Assets/Scripts/ServerShared/Scenarios/Crossing.cs +++ b/Assets/Scripts/ServerShared/Scenarios/Crossing.cs @@ -6,8 +6,8 @@ public sealed class Crossing : Scenario public override string Name => "Crossing"; public override string Id => "crossing"; public override string Brief => "A Lightning Projector at the starboard gun and a Cell Blaster at the port gun, an unpiloted Djinni coasting across the bow " + - "close ahead and an unpiloted one waiting at range. The crosser is launched to port at 50 m/s and only coasts: nothing steers it, " + - "its hull drag slows it to about 25 m/s at the bow line, and it does not shoot back. Make the pass coasting: give no throttle until the Djinni has crossed the bow, then fly freely " + + "close ahead and an unpiloted one waiting at range. The crosser is launched to port at 25 m/s and only coasts: nothing steers it, " + + "a hull coasts at its launch speed below its top speed, and it does not shoot back. Make the pass coasting: give no throttle until the Djinni has crossed the bow, then fly freely " + "(thrusting from the start carries you past it). Verify: as the Djinni crosses close, FastBlast lands some and projector " + "almost none; against the Djinni at range both land. Fly it again without the Fire Control Array: " + "with it the small gun lands more."; @@ -16,13 +16,15 @@ public sealed class Crossing : Scenario // Where the Djinni sit: the player is at Origin facing +z. Origin is 250 m or more from every body, where the zone's // pull is weakest (about 1.4 u/s^2; the sum of the wells never drops to nothing, and at the old origin the player sat on the star). - // The crosser is unpiloted and placed moving at the Djinni's 50 m/s cap, so its pass is ballistic: bent by the field and slowed - // by hull drag (it reaches the bow line at about 25 m/s relative to the player, 3.6 s in), and nothing steers it. + // The crosser is unpiloted and placed moving at 25 m/s, well under the Djinni's 50 m/s top speed, so its pass is ballistic: a hull + // coasts below its top speed (aetheria-gravity:ruling:no-drag-below-top-speed) and only the field bends it, so it reaches the + // bow line at 25 m/s relative to the player, 3.6 s in, and nothing steers it. (Launched at the cap with drag slowing it to 25 m/s, + // as this scenario was first tuned, it would now hold 50 m/s and cross in 2.6 s.) // CrosserAt is the centre of the start window in which the pass holds: shifting it by 10 m along either axis keeps the pass // inside the test's time and distance windows (CrossingHoldsAcrossTheStartWindow). // The waiting Djinni sits inside the Lightning Projector's arc at 80% of its Range. public static readonly CultMath.float2 Origin = float2(724, 124); - public static readonly CultMath.float2 CrosserAt = Origin + float2(-128, 45), CrosserFacing = float2(1, 0), CrosserVelocity = float2(50, 0); + public static readonly CultMath.float2 CrosserAt = Origin + float2(-90, 45), CrosserFacing = float2(1, 0), CrosserVelocity = float2(25, 0); // The fighter is staged at this quality, so its gear and the range below do not ride the item stream. public const float StagedQuality = .6f; // The Lightning Projector's Range in metres at the staged fit: 996.99 after the three seconds the test settles for diff --git a/tests/Aetheria.Shared.Tests/CrossingScenarioTests.cs b/tests/Aetheria.Shared.Tests/CrossingScenarioTests.cs index 9c827b968..efaa4998d 100644 --- a/tests/Aetheria.Shared.Tests/CrossingScenarioTests.cs +++ b/tests/Aetheria.Shared.Tests/CrossingScenarioTests.cs @@ -10,7 +10,7 @@ using float3 = CultMath.float3; public sealed partial class RunStartTests { // Crossing: the demo's cast; projector and Cell Blaster fitted in separate weapon groups with the Fire Control Array - // aboard; an unpiloted hostile Djinni, coasting at the Djinni's speed cap, that crosses the bow inside 400 m; and an unpiloted neutral one inside the Lightning Projector's + // aboard; an unpiloted hostile Djinni, coasting at 25 m/s, that crosses the bow inside 400 m; and an unpiloted neutral one inside the Lightning Projector's // arc, short of its Range and beyond three quarters of it. [Fact] public void CrossingConditions() @@ -43,12 +43,12 @@ public sealed partial class RunStartTests Assert.True(toCrosser.z > 0, "the crosser starts ahead"); Assert.True(abs(dot(crosser.Direction, player.Direction)) <= sin(radians(30f)) + 1e-4f, "the crosser faces within 30 degrees of perpendicular to the player's bow"); - // It is placed moving: toward the bow line at most of the Djinni's 50 m/s cap, so its run is its own and not only the + // It is placed moving: toward the bow line at the scenario's 25 m/s, so its run is its own and not only the // arena's pull on the player. var crosserVelocity = ((Ship) crosser).Velocity; Assert.True(crosserVelocity.x * toCrosser.x < 0, "the crosser starts moving toward the bow line"); - Assert.True(abs(crosserVelocity.x) >= 40f, "the crosser's speed toward the bow line is most of the Djinni's speed cap"); - Assert.True(length(crosserVelocity) >= 40f, "the crosser starts at most of the Djinni's speed cap"); + Assert.Equal(25f, abs(crosserVelocity.x), 3); + Assert.Equal(25f, length(crosserVelocity), 3); Assert.False(player.IsHostileTo(waiting) || waiting.IsHostileTo(player), "the waiting Djinni is neutral"); Assert.Equal(0, AgentsOf(arena, waiting)); @@ -71,7 +71,7 @@ public sealed partial class RunStartTests Assert.True(length(pass.Ahead) < CrossingMetres, "the crosser passes inside the stated distance"); Assert.True(FireControl.InArc(small, float3(pass.Ahead.x, 0, pass.Ahead.y)), "Cell Blaster bears on the crosser as it crosses"); Assert.True(dot(FireControl.MountDirection(small), float3(player.Direction.x, 0, player.Direction.y)) > 0, "Cell Blaster mounts forward"); - // At seed 112 with no input the pass is at about 3.6 s and 45 m; a seed, field or staging change that loses it moves these. + // At seed 112 with no input the pass is at about 3.6 s and 45 m, at 25 m/s relative (a hull under its top speed coasts, so the launch speed holds); a seed, field or staging change that loses it moves these. Assert.InRange(pass.Seconds, CrossingEarliest, CrossingLatest); Assert.InRange(length(pass.Ahead), 30f, 60f); }