2026-10-11 18:52:35 +02:00
using static CultMath . math ;
using static ItemRotation ;
public sealed class Crossing : Scenario
{
public override string Name = > "Crossing" ;
2026-10-15 15:22:14 +02:00
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 " +
2026-10-15 15:28:56 +02:00
"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 " +
2026-10-15 15:22:14 +02:00
"(thrusting from the start carries you past it). Verify: as the Djinni crosses close, FastBlast lands some and projector " +
2026-10-11 18:52:35 +02:00
"almost none; against the Djinni at range both land. Fly it again without the Fire Control Array: " +
"with it the small gun lands more." ;
public override uint Seed = > 112 ;
public override bool Ambient = > false ;
2026-10-15 06:36:01 +02:00
// 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).
2026-10-15 15:28:56 +02:00
// 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.
// 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).
2026-10-15 15:22:14 +02:00
// The waiting Djinni sits inside the Lightning Projector's arc at 80% of its Range.
2026-10-15 06:36:01 +02:00
public static readonly CultMath . float2 Origin = float2 ( 724 , 124 ) ;
2026-10-15 15:28:56 +02:00
public static readonly CultMath . float2 CrosserAt = Origin + float2 ( - 128 , 45 ) , CrosserFacing = float2 ( 1 , 0 ) , CrosserVelocity = float2 ( 50 , 0 ) ;
Briefs, comments and tests name current gear; generic-designs lands only its own records and rewrites the last flavour and brand design texts
Scenario Briefs (Crossing, Duel, FusedRounds, LauncherAngles, StarvedReactor, WeaponFeel), scenario and test comments and test locals say the design names (Lightning Projector, Light Laser, Cell Blaster, Guided Missile Launcher, Swarm Rocket Pod, Muon Fusion Reactor, Cluster Cannon, Charged Scatter Blaster, Rugged Drive, Racing Drive) instead of the retired brand names; Crossing.PlightRange is ProjectorRange.
generic-designs: the batch carries ExpectUnchanged so every record it does not author keeps its bytes (the plain commit re-encoded four records, dropping SensorData key 7). DesignTexts rewrites Large Drive, Tractor Beam, Smoke Proximity, Smoke Delayed, Smoke Refused and Smoke Refused Charge as tech-node text; the Tractor Beam's old line moves to its product Space Tractor. smoke-rounds' authored text names Light Laser and Charged Scatter Blaster to match.
Tests: NoScenarioBriefNamesRetiredGear, TheCapacitorDesignCarriesItsRoleMap, NoDesignTextNamesAProduct, and a command test that widens every payload and checks apply changes only the three records it authors and restores the rewritten texts.
Not built or run at this commit; GameData/Aetheria.cc is regenerated in the next commit, so NoDesignTextNamesAProduct and the command test fail here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:34:08 +02:00
public const float ProjectorRange = 1216f ; // the Lightning Projector's Range in metres, 1216.4 as the fit stands settled at rest
2026-10-15 06:36:01 +02:00
public static readonly CultMath . float2 Waiting = Origin + float2 ( 0 , ProjectorRange * . 8f ) ;
2026-10-11 18:52:35 +02:00
public override Galaxy Generate ( GalaxyStage stage ) = > stage . Prelude ( DemoTerminus . Cast ) ;
public override void Stage ( ScenarioStage stage )
{
var fighter = stage . Fit ( "Longinus" ,
( "Cockpit 2x2" , int2 ( 2 , 6 ) , None ) ,
( "Large Drive" , int2 ( 1 , 0 ) , Reversed ) ,
( "Large Drive" , int2 ( 3 , 0 ) , Reversed ) ,
2026-10-13 20:29:10 +02:00
( "Rugged Drive" , int2 ( 2 , 14 ) , CounterClockwise ) ,
( "Rugged Drive" , int2 ( 3 , 14 ) , Clockwise ) ,
( "Compact Reactor" , int2 ( 2 , 4 ) , None ) ,
( "Guided Missile Launcher" , int2 ( 0 , 5 ) , None ) ,
( "Guided Missile Launcher" , int2 ( 5 , 5 ) , None ) ,
( "Lightning Projector" , int2 ( 1 , 8 ) , None ) ,
( "Cell Blaster" , int2 ( 4 , 8 ) , None ) ,
( "Radiator" , int2 ( 2 , 2 ) , CounterClockwise ) ,
( "Radiator" , int2 ( 3 , 2 ) , Clockwise ) ,
( "Compact Sensor" , int2 ( 3 , 10 ) , None ) ,
2026-10-11 18:52:35 +02:00
( "Fire Control Array" , int2 ( 1 , 6 ) , Clockwise ) ) ;
2026-10-15 06:36:01 +02:00
stage . Player ( fighter , Origin , facing : float2 ( 0 , 1 ) ) ;
2026-10-15 15:22:14 +02:00
stage . Place ( stage . Generated ( "Djinni" ) , CrosserAt , facing : CrosserFacing , stance : ScenarioStance . Hostile , velocity : CrosserVelocity ) ;
2026-10-11 18:52:35 +02:00
stage . Place ( stage . Bare ( "Djinni" ) , Waiting ) ;
}
}