Smoke Test Hull: 2x2 side mounts on a two-cell margin; test classes read sold hulls only
The 1x1 Ballistic side mount tripped IsFilledByAgreesWithACellOracle (Shape.FitsWithin never places a design whose only cell is not its origin, so Takes and the oracle disagree on a 1x1 hardpoint); the side mounts are 2x2 now, which needs two columns of margin on each side, so every Longinus cell and hardpoint moves right by two. The two catalog-wide tests that listed every hull now list sold hulls. This commit does not pass the suite: GameData/Aetheria.cc still holds the previous hull until the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6b0f15d76a
commit
043a73eb0a
8 changed files with 68 additions and 57 deletions
|
|
@ -24,22 +24,22 @@ public sealed class Arcs : Scenario
|
|||
public override void Stage(ScenarioStage stage)
|
||||
{
|
||||
var fighter = stage.Fit("Smoke Test Hull",
|
||||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("GT 3K", int2(0, 5), None),
|
||||
("GT 3K", int2(5, 5), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("FastBlast+-", int2(4, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise),
|
||||
("Point Defense Gun", int2(5, 4), Clockwise),
|
||||
("Point Defense Laser", int2(0, 4), CounterClockwise));
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("GT 3K", int2(2, 5), None),
|
||||
("GT 3K", int2(7, 5), None),
|
||||
("FastBlast+-", int2(3, 8), None),
|
||||
("FastBlast+-", int2(6, 8), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise),
|
||||
("Autocannon", int2(8, 4), Clockwise),
|
||||
("ChargeBlast SG", int2(0, 4), CounterClockwise));
|
||||
var turret = stage.Fit("Turret",
|
||||
("ClearPath", int2(1, 2), None),
|
||||
("ClearPath", int2(5, 2), None),
|
||||
|
|
|
|||
|
|
@ -18,19 +18,19 @@ public sealed class WeaponFeel : Scenario
|
|||
public override void Stage(ScenarioStage stage)
|
||||
{
|
||||
var shooter = stage.Fit("Smoke Test Hull",
|
||||
("Cockpit 2x2", int2(2, 6), None),
|
||||
("Large Drive", int2(1, 0), Reversed),
|
||||
("Cockpit 2x2", int2(4, 6), None),
|
||||
("Large Drive", int2(3, 0), Reversed),
|
||||
("Talaria", int2(2, 14), CounterClockwise),
|
||||
("Talaria", int2(3, 14), Clockwise),
|
||||
("Core Power", int2(2, 4), None),
|
||||
("ChargeBlast SG", int2(1, 17), None),
|
||||
("Autocannon", int2(3, 17), None),
|
||||
("FastBlast+-", int2(1, 8), None),
|
||||
("Iapyx", int2(2, 2), CounterClockwise),
|
||||
("Iapyx", int2(3, 2), Clockwise),
|
||||
("not if i see you first", int2(3, 10), None),
|
||||
("Fire Control Array", int2(1, 6), Clockwise));
|
||||
("Large Drive", int2(5, 0), Reversed),
|
||||
("Talaria", int2(4, 14), CounterClockwise),
|
||||
("Talaria", int2(5, 14), Clockwise),
|
||||
("Core Power", int2(4, 4), None),
|
||||
("ChargeBlast SG", int2(3, 17), None),
|
||||
("Autocannon", int2(5, 17), None),
|
||||
("FastBlast+-", int2(3, 8), None),
|
||||
("Iapyx", int2(4, 2), CounterClockwise),
|
||||
("Iapyx", int2(5, 2), Clockwise),
|
||||
("not if i see you first", int2(5, 10), None),
|
||||
("Fire Control Array", int2(3, 6), Clockwise));
|
||||
|
||||
stage.Player(shooter, float2(0, 0), facing: float2(0, 1));
|
||||
stage.Place(stage.Bare("Longinus"), Target);
|
||||
|
|
|
|||
|
|
@ -248,13 +248,14 @@ public sealed class RestoredHullsTests
|
|||
}
|
||||
|
||||
// The dock intro dereferences the ship's VelocityLimit unguarded (ActionGameManager), so every ship hull the catalog
|
||||
// ships carries exactly one, with a top speed that is a finite positive number. After the retirement
|
||||
// sells carries exactly one, with a top speed that is a finite positive number. After the retirement
|
||||
// (ruling retire-longinusx) the shipped ship hulls are the Longinus and the Djinni.
|
||||
[Fact]
|
||||
public void EveryShippedShipHullHasATopSpeed()
|
||||
{
|
||||
using var cache = OpenCatalog();
|
||||
var ships = cache.GetAll<HullData>().Where(h => h.HullType == HullType.Ship).OrderBy(h => h.Name, StringComparer.Ordinal).ToArray();
|
||||
var sold = cache.GetAll<FactionProductData>().Select(product => product.Design.Key).ToHashSet();
|
||||
var ships = cache.GetAll<HullData>().Where(h => h.HullType == HullType.Ship && sold.Contains(cache.RefOf(h).Key)).OrderBy(h => h.Name, StringComparer.Ordinal).ToArray();
|
||||
Assert.Equal(new[] { "Djinni", "Longinus" }, ships.Select(h => h.Name));
|
||||
foreach (var hull in ships)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -439,13 +439,13 @@ public sealed partial class RunStartTests : IDisposable
|
|||
Assert.True(disagreements.Count == 0, string.Join("; ", disagreements.Take(10)) + $" ({disagreements.Count} in all)");
|
||||
}
|
||||
|
||||
// The sold hardpoint gear that no hull fits even loosely today, stated so that authoring a hull that takes one
|
||||
// shows up here as a changed list.
|
||||
// The sold hardpoint gear that no sold hull fits even loosely today (a test hull no product sells gives no gear a
|
||||
// home), stated so that authoring a hull that takes one shows up here as a changed list.
|
||||
[Fact]
|
||||
public void TheSoldHardpointGearWithNoHomeToday()
|
||||
{
|
||||
var hulls = _cache.GetAll<HullData>().ToArray();
|
||||
var sold = _cache.GetAll<FactionProductData>().Select(product => product.Design.Key).ToHashSet();
|
||||
var hulls = _cache.GetAll<HullData>().Where(hull => sold.Contains(_cache.RefOf(hull).Key)).ToArray();
|
||||
var homeless = _cache.GetAll<EquippableItemData>()
|
||||
.Where(design => sold.Contains(_cache.RefOf(design).Key) && design.Price > 0)
|
||||
.Where(design => design.HardpointType != HardpointType.Tool && design.HardpointType != HardpointType.Hull)
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ public sealed partial class RunStartTests
|
|||
var turret = Assert.IsType<OrbitalEntity>(staged.Entities[2]);
|
||||
Assert.Equal((Arcs.Bow, Arcs.Stern, Arcs.Turret), (bow.Position.xz, stern.Position.xz, turret.Position.xz));
|
||||
|
||||
var mounts = player.Equipment.Where(item => item.Behaviors.OfType<Weapon>().Any() && item.Hardpoint.Rotation == ItemRotation.None).ToList();
|
||||
var mounts = player.Equipment.Where(item => item.Behaviors.OfType<Weapon>().Any() && item.EquippableItem.Rotation == ItemRotation.None).ToList();
|
||||
Assert.NotEmpty(mounts);
|
||||
foreach (var mount in mounts)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public sealed class SmokeHullCommandTests : IDisposable
|
|||
var hulls = db.Cache.GetAll<HullData>().Where(h => h.Name == HullName).ToArray();
|
||||
var keys = hulls.Select(h => db.Cache.RefOf(h).Key).ToArray();
|
||||
var products = db.Cache.GetAll<FactionProductData>().Count(p => keys.Any(k => p.Design.Key.Equals(k)));
|
||||
var fitted = new[] { "ChargeBlast SG", "Autocannon", "Point Defense Gun", "Point Defense Laser" }
|
||||
var fitted = new[] { "ChargeBlast SG", "Autocannon" }
|
||||
.Select(name => db.Cache.GetAll<EquippableItemData>().Single(d => d.Name == name)).ToArray();
|
||||
return (hulls, db.Cache.GetAll<HullData>().Single(h => h.Name == "Longinus"), fitted, products);
|
||||
}
|
||||
|
|
@ -61,14 +61,17 @@ public sealed class SmokeHullCommandTests : IDisposable
|
|||
var hull = Assert.Single(hulls);
|
||||
Assert.Equal(0, products);
|
||||
|
||||
// The Longinus's own hardpoints survive, and four are added: each takes the gear the scenarios fit to it.
|
||||
// The Longinus's own hardpoints survive (moved over by the margin the flank blocks need), and four are added: each takes
|
||||
// the gear the scenarios fit to it.
|
||||
Assert.Equal(longinus.Hardpoints.Count + 4, hull.Hardpoints.Count);
|
||||
var added = hull.Hardpoints.Skip(longinus.Hardpoints.Count).ToArray();
|
||||
var (chargeBlast, autocannon, pdGun, pdLaser) = (fitted[0], fitted[1], fitted[2], fitted[3]);
|
||||
var (chargeBlast, autocannon) = (fitted[0], fitted[1]);
|
||||
Assert.Equal(longinus.Hardpoints.Select(h => (h.Type, h.Position.x + 2, h.Position.y, h.Rotation)),
|
||||
hull.Hardpoints.Take(longinus.Hardpoints.Count).Select(h => (h.Type, h.Position.x, h.Position.y, h.Rotation)));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Energy && h.Rotation == ItemRotation.None && h.Takes(chargeBlast));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Ballistic && h.Rotation == ItemRotation.None && h.Takes(autocannon));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Ballistic && h.Rotation == ItemRotation.Clockwise && h.Takes(pdGun));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Energy && h.Rotation == ItemRotation.CounterClockwise && h.Takes(pdLaser));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Ballistic && h.Rotation == ItemRotation.Clockwise && h.Takes(autocannon));
|
||||
Assert.Single(added, h => h.Type == HardpointType.Energy && h.Rotation == ItemRotation.CounterClockwise && h.Takes(chargeBlast));
|
||||
foreach (var hardpoint in added)
|
||||
foreach (var cell in hardpoint.Shape.Coordinates)
|
||||
Assert.True(hull.Shape[hardpoint.Position + cell], "a new hardpoint lies outside the hull's cells");
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ public sealed partial class RunStartTests
|
|||
Assert.Equal((Arcs.Port, Arcs.Starboard), (port.Position.xz, starboard.Position.xz));
|
||||
|
||||
var mounts = WeaponMounts(player);
|
||||
var forward = mounts.Where(item => item.Hardpoint.Rotation == ItemRotation.None).ToList();
|
||||
var starboardMounts = mounts.Where(item => item.Hardpoint.Rotation == ItemRotation.Clockwise).ToList();
|
||||
var portMounts = mounts.Where(item => item.Hardpoint.Rotation == ItemRotation.CounterClockwise).ToList();
|
||||
var forward = mounts.Where(item => item.EquippableItem.Rotation == ItemRotation.None).ToList();
|
||||
var starboardMounts = mounts.Where(item => item.EquippableItem.Rotation == ItemRotation.Clockwise).ToList();
|
||||
var portMounts = mounts.Where(item => item.EquippableItem.Rotation == ItemRotation.CounterClockwise).ToList();
|
||||
Assert.NotEmpty(forward);
|
||||
Assert.NotEmpty(starboardMounts);
|
||||
Assert.NotEmpty(portMounts);
|
||||
|
|
|
|||
|
|
@ -1606,8 +1606,9 @@ public static class Program
|
|||
// Smoke Test Hull, a copy of the Longinus hull that no product sells, so no generated ship, station or shop offers
|
||||
// it and only a scenario fits it. It adds what no shipped ship hull has, where the Longinus runs out of cells: two
|
||||
// rows of cells past the bow carrying a 2x2 Energy hardpoint (ChargeBlast SG) and a 2x2 Ballistic hardpoint
|
||||
// (Autocannon), and a 1x1 side mount on each flank, a Clockwise Ballistic one on the starboard and a
|
||||
// CounterClockwise Energy one on the port. The Longinus's own hardpoints are untouched. Its prefab is the
|
||||
// (Autocannon), and a 2x2 side mount on each flank, a Clockwise Ballistic one on the starboard and a
|
||||
// CounterClockwise Energy one on the port, which need two columns of margin on each side: every Longinus cell and
|
||||
// hardpoint moves right by two. Their gear is unchanged. Its prefab is the
|
||||
// Longinus's, which has no barrel for the new hardpoints: they fire from the hull centre. Dry run unless passed
|
||||
// "apply".
|
||||
public static int SmokeHullCatalog(bool apply, string root = null)
|
||||
|
|
@ -1626,26 +1627,32 @@ public static class Program
|
|||
hull.Name = name;
|
||||
hull.Description = "The Longinus with two forward 2x2 hardpoints and a side mount on each flank, for scenarios. No faction builds it.";
|
||||
|
||||
// Two rows of cells past the bow, over the width of the tail's neighbours (x 1..4).
|
||||
var grown = new Shape(longinus.Shape.Width, longinus.Shape.Height + 2);
|
||||
// Two cells of margin on each side (every Longinus cell and hardpoint moves right by Margin), two rows of cells past
|
||||
// the bow over the width of the tail's neighbours, and a 2x2 block of cells on each flank at the wide section.
|
||||
const int Margin = 2;
|
||||
var width = longinus.Shape.Width + 2 * Margin;
|
||||
var bow = longinus.Shape.Height;
|
||||
var grown = new Shape(width, bow + 2);
|
||||
for (var x = 0; x < longinus.Shape.Width; x++)
|
||||
for (var y = 0; y < longinus.Shape.Height; y++) grown.Cells[x, y] = longinus.Shape.Cells[x, y];
|
||||
for (var x = 1; x <= 4; x++)
|
||||
for (var y = longinus.Shape.Height; y < grown.Height; y++) grown.Cells[x, y] = true;
|
||||
for (var y = 0; y < bow; y++) grown.Cells[x + Margin, y] = longinus.Shape.Cells[x, y];
|
||||
for (var x = Margin + 1; x <= Margin + 4; x++)
|
||||
for (var y = bow; y < grown.Height; y++) grown.Cells[x, y] = true;
|
||||
foreach (var x in new[] { 0, 1, width - 2, width - 1 })
|
||||
for (var y = 4; y <= 5; y++) grown.Cells[x, y] = true;
|
||||
hull.Shape = grown;
|
||||
foreach (var existing in hull.Hardpoints) existing.Position = CultMath.math.int2(existing.Position.x + Margin, existing.Position.y);
|
||||
|
||||
static Shape Cells(int width, int height)
|
||||
static Shape Cells(int side)
|
||||
{
|
||||
var shape = new Shape(width, height);
|
||||
for (var x = 0; x < width; x++)
|
||||
for (var y = 0; y < height; y++) shape.Cells[x, y] = true;
|
||||
var shape = new Shape(side, side);
|
||||
for (var x = 0; x < side; x++)
|
||||
for (var y = 0; y < side; y++) shape.Cells[x, y] = true;
|
||||
return shape;
|
||||
}
|
||||
var bow = longinus.Shape.Height;
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Energy, Position = CultMath.math.int2(1, bow), Shape = Cells(2, 2), Rotation = ItemRotation.None, Armor = 20f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Ballistic, Position = CultMath.math.int2(3, bow), Shape = Cells(2, 2), Rotation = ItemRotation.None, Armor = 20f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Ballistic, Position = CultMath.math.int2(longinus.Shape.Width - 1, 4), Shape = Cells(1, 1), Rotation = ItemRotation.Clockwise, Armor = 10f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Energy, Position = CultMath.math.int2(0, 4), Shape = Cells(1, 1), Rotation = ItemRotation.CounterClockwise, Armor = 10f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Energy, Position = CultMath.math.int2(Margin + 1, bow), Shape = Cells(2), Rotation = ItemRotation.None, Armor = 20f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Ballistic, Position = CultMath.math.int2(Margin + 3, bow), Shape = Cells(2), Rotation = ItemRotation.None, Armor = 20f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Ballistic, Position = CultMath.math.int2(width - 2, 4), Shape = Cells(2), Rotation = ItemRotation.Clockwise, Armor = 20f });
|
||||
hull.Hardpoints.Add(new HardpointData { Type = HardpointType.Energy, Position = CultMath.math.int2(0, 4), Shape = Cells(2), Rotation = ItemRotation.CounterClockwise, Armor = 20f });
|
||||
|
||||
Console.WriteLine($"Adds \"{name}\": a Longinus copy grown from {longinus.Shape.Width}x{longinus.Shape.Height} to {grown.Width}x{grown.Height}, no product.");
|
||||
foreach (var hardpoint in hull.Hardpoints.Skip(longinus.Hardpoints.Count))
|
||||
|
|
|
|||
Loading…
Reference in a new issue