Drop Broadside (no catalog hull has side-rotated gun hardpoints); fix Cut5 arc fixtures for solutions

Question aetheria-release❓broadside-hull-source is raised on the spec. The Cut5 arc tests now
aim astern so only a solution can fire the gun (the aim used to be zero, which is free fire).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-08 13:37:15 +02:00
commit 538b11b4ea
5 changed files with 4 additions and 75 deletions

View file

@ -1,37 +0,0 @@
using static CultMath.math;
using static ItemRotation;
public sealed class Broadside : Scenario
{
public override string Name => "Broadside";
public override string Brief => "A hull with a port gun and a starboard gun in separate groups, bare hulls abeam to port and starboard and one ahead. " +
"Verify: designate the starboard hull, aim ahead and fire the starboard group: it fires on the hull without the aim. " +
"Fire the port group: it holds, because nothing bears and the aim is outside its arc. Undesignate and aim to port: " +
"the port group fires free along the aim.";
public override uint Seed => 104;
public override bool Ambient => false;
// Where the targets sit: the player is at the origin facing +z.
public static readonly CultMath.float2 Port = float2(-400, 0), Starboard = float2(400, 0), Ahead = float2(0, 400);
public override Galaxy Generate(GalaxyStage stage) => stage.Prelude();
public override void Stage(ScenarioStage stage)
{
var broadside = stage.Fit("LonginusX",
("Cockpit 2x2", int2(2, 6), None),
("Traction", int2(2, 3), None),
("Core Power", int2(2, 1), None),
("GT 3K", int2(0, 5), CounterClockwise),
("FastBlast+-", int2(4, 8), Clockwise),
("Iapyx", int2(1, 2), CounterClockwise),
("Iapyx", int2(4, 2), Clockwise),
("not if i see you first", int2(3, 10), None),
("Fire Control Array", int2(2, 5), None));
stage.Player(broadside, float2(0, 0), facing: float2(0, 1));
stage.Place(stage.Bare("LonginusX"), Port);
stage.Place(stage.Bare("LonginusX"), Starboard);
stage.Place(stage.Bare("LonginusX"), Ahead);
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9fb4b5cf800b4ccdae5d6c15448df36e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -6,6 +6,6 @@ public static class Scenarios
public static readonly Scenario[] Tests =
{
new DjinniShakedown(), new StarvedReactor(), new Arcs(), new Broadside(), new Duel(), new LauncherAngles(), new LongHaul()
new DjinniShakedown(), new StarvedReactor(), new Arcs(), new Duel(), new LauncherAngles(), new LongHaul()
};
}

View file

@ -364,6 +364,7 @@ public sealed class FireControlCut5Tests : IDisposable
var settings = TestSettings();
settings.FiringArc = 60; // narrow -- an abeam target is well outside it
var e = BuildBeam(settings, float3(100, 0, 0)); // directly abeam
e.Shooter.Aim = float3(0, 0, -1); // astern, outside the arc: nothing bears and the aim is not free
e.Weapon.Activate();
Assert.True(e.Weapon.Firing); // Activate() sets _firing unconditionally, same as InstantWeapon.Trigger
@ -466,6 +467,7 @@ public sealed class FireControlCut5Tests : IDisposable
shooter.Position = float3.zero;
target.Position = float3(100, 0, 0); // directly abeam
shooter.SetTarget(target);
shooter.Aim = float3(0, 0, -1); // astern, outside the arc: only a solution can fire the gun
var weapon = (Weapon) weaponItem.Behaviors.Single(b => b is Weapon);
Assert.False(weapon.ArcAllowsFire);
@ -489,6 +491,7 @@ public sealed class FireControlCut5Tests : IDisposable
e.Shooter.Position = float3.zero;
e.Target.Position = float3(100, 0, 0); // abeam -- outside the narrow 60-degree arc
e.Shooter.SetTarget(e.Target);
e.Shooter.Aim = float3(0, 0, -1); // astern, outside the arc: nothing bears and the aim is not free
var resolvedCount = 0;
e.Zone.ShotResolved.Subscribe(_ => resolvedCount++);

View file

@ -500,32 +500,6 @@ public sealed partial class RunStartTests
Assert.False(player.IsHostileTo(bow) || player.IsHostileTo(stern), "the bare hulls are neutral");
}
// Broadside's geometry: the player carries guns on both sides in separate groups. Each side's gun bears on the hull
// abeam on its side and on nothing else, and the hull ahead bears for neither.
[Fact]
public void BroadsideGeometry()
{
var staged = Stage(Arena(false), new Broadside());
var player = staged.Player;
var port = staged.Entities[0];
var starboard = staged.Entities[1];
var ahead = staged.Entities[2];
Assert.Equal((Broadside.Port, Broadside.Starboard, Broadside.Ahead), (port.Position.xz, starboard.Position.xz, ahead.Position.xz));
var groups = player.WeaponGroups.Where(group => group.items.Count > 0).ToList();
Assert.True(groups.Count >= 2, "the guns must sit in separate groups");
var portGun = Assert.Single(groups.SelectMany(group => group.items).Where(item => item.EquippableItem.Rotation == ItemRotation.CounterClockwise && item.Behaviors.OfType<Weapon>().Any()));
var starboardGun = Assert.Single(groups.SelectMany(group => group.items).Where(item => item.EquippableItem.Rotation == ItemRotation.Clockwise && item.Behaviors.OfType<Weapon>().Any()));
Assert.NotEqual(groups.Single(group => group.items.Contains(portGun)).items, groups.Single(group => group.items.Contains(starboardGun)).items);
Assert.True(FireControl.InArc(portGun, port.Position - player.Position));
Assert.False(FireControl.InArc(portGun, starboard.Position - player.Position));
Assert.False(FireControl.InArc(portGun, ahead.Position - player.Position));
Assert.True(FireControl.InArc(starboardGun, starboard.Position - player.Position));
Assert.False(FireControl.InArc(starboardGun, port.Position - player.Position));
Assert.False(FireControl.InArc(starboardGun, ahead.Position - player.Position));
}
// Q3 (operator, 2026-09-30): a design no product sells is a scenario test design and nothing else. A test design
// is one some scenario's staged entities carry, fitted or in cargo. These are the unsold designs none carries.
private List<string> UnsoldAndUnplaced(IEnumerable<RunStart.Staged> runs)