Ship mounts: delete EquipmentHardpoints and the plan's Equipment list

The array's only reader was the dead SoundBank block in EntityInstance, which
looked up a hardpoint transform and did nothing with it. ShipModPlan.Equipment,
ShipModShips' assignment, the preview check, the FBX builder's HP.Equipment
case and its smoke assertion all fed that one dead reader. An HP.Equipment node
is now refused by the builder as an unknown hardpoint kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-03 14:34:05 +02:00
commit 23976aada7
8 changed files with 7 additions and 31 deletions

View file

@ -53,7 +53,7 @@ public static class ShipModPreview
var clone = UnityEngine.Object.Instantiate(ship.gameObject).GetComponent<ShipInstance>();
try { CheckShip(clone, plan, "clone"); }
finally { UnityEngine.Object.DestroyImmediate(clone.gameObject); }
Debug.Log($"SHIP_MOD_ASSEMBLY_SMOKE equipment={plan.Equipment.Length} thrusters={plan.Thrusters.Length} " +
Debug.Log($"SHIP_MOD_ASSEMBLY_SMOKE thrusters={plan.Thrusters.Length} " +
$"weapons={plan.Weapons.Length} radiators={plan.Radiators.Length}");
}
@ -64,8 +64,6 @@ public static class ShipModPreview
Require(ship.HullColliders.Length == 1 && Inside(ship.HullColliders[0]) &&
ship.HullColliders[0].GetComponent<MeshCollider>() != null, "one hull collider with a MeshCollider");
Require(Inside(ship.MapIcon) && Inside(ship.Shield) && Inside(ship.TractorBeam), "map icon, shield and tractor inside the ship");
Require(ship.EquipmentHardpoints.Select(node => node.name).SequenceEqual(plan.Equipment) &&
ship.EquipmentHardpoints.All(Inside), "equipment hardpoints named by the plan");
Require(ship.ThrusterHardpoints.Select(node => node.name).SequenceEqual(plan.Thrusters) &&
ship.ThrusterHardpoints.All(node => Inside(node) && Inside(node.Emitter)), "thruster hardpoints with emitters");
Require(ship.RadiatorHardpoints.Select(node => node.name).SequenceEqual(plan.Radiators) &&

View file

@ -92,7 +92,7 @@ public static class ShipPrefabAuthoring
var combatLayer = LayerMask.NameToLayer("Combat");
if (minimapLayer < 0 || combatLayer < 0) throw new InvalidOperationException("Minimap or Combat layer is missing");
var equipment = new List<Transform>();
var hardpoints = 0;
var thrusters = new List<ThrusterHardpoint>();
var weapons = new List<WeaponHardpoint>();
var radiators = new List<RadiatorHardpoint>();
@ -106,10 +106,9 @@ public static class ShipPrefabAuthoring
var parts = name.Split('.');
if (parts.Length != 3 || string.IsNullOrWhiteSpace(parts[2]))
throw new InvalidOperationException($"Invalid hardpoint name {name}");
equipment.Add(node);
hardpoints++;
switch (parts[1])
{
case "Equipment": break;
case "Thruster":
var emitter = DirectChild(node, "Emitter")?.GetComponent<MeshRenderer>();
if (emitter == null) throw new InvalidOperationException($"{name} needs an Emitter mesh child");
@ -155,7 +154,7 @@ public static class ShipPrefabAuthoring
name != "SHIP.Shield" && name != "SHIP.TractorBeam")
throw new InvalidOperationException($"Unknown ship anchor {name}");
}
if (equipment.Count == 0) throw new InvalidOperationException("Ship has no HP.* hardpoints");
if (hardpoints == 0) throw new InvalidOperationException("Ship has no HP.* hardpoints");
mapIcon.sharedMaterial = mapMaterial;
mapIcon.gameObject.layer = minimapLayer;
@ -182,7 +181,6 @@ public static class ShipPrefabAuthoring
ship.InvisibleMaterial = invisible;
ship.Shield = shield;
ship.HullColliders = new[] { hullSurface };
ship.EquipmentHardpoints = equipment.ToArray();
ship.ThrusterHardpoints = thrusters.ToArray();
ship.WeaponHardpoints = weapons.ToArray();
ship.RadiatorHardpoints = radiators.ToArray();

View file

@ -23,13 +23,11 @@ public static class ShipPrefabAuthoringSmoke
Child(weapon.gameObject, "Muzzle.0");
var radiator = Child(root, "HP.Radiator.Port");
Primitive(radiator.gameObject, "RadiatorMesh");
Child(root, "HP.Equipment.Reactor");
Child(root, "Pivot.2.-90.90.-80.80.60");
ShipPrefabAuthoring.Configure(root);
var ship = root.GetComponent<ShipInstance>();
Require(ship != null && ship.EquipmentHardpoints.Length == 4, "equipment references");
Require(ship.ThrusterHardpoints.Length == 1 && ship.ThrusterHardpoints[0].Emitter != null, "thruster emitter");
Require(ship != null && ship.ThrusterHardpoints.Length == 1 && ship.ThrusterHardpoints[0].Emitter != null, "thruster emitter");
Require(ship.WeaponHardpoints.Length == 1 && ship.WeaponHardpoints[0].FiringPoint.Length == 2, "weapon muzzles");
Require(ship.WeaponHardpoints[0].FiringPoint[0].name == "Muzzle.0", "numeric muzzle order");
Require(ship.RadiatorHardpoints.Length == 1 && ship.RadiatorHardpoints[0].Mesh != null, "radiator mesh");

View file

@ -20,7 +20,6 @@ public class EntityInstance : MonoBehaviour
public ShieldManager Shield;
public HullCollider[] HullColliders;
public Transform[] EquipmentHardpoints;
public RadiatorHardpoint[] RadiatorHardpoints;
public ThrusterHardpoint[] ThrusterHardpoints;
public WeaponHardpoint[] WeaponHardpoints;
@ -241,16 +240,6 @@ public class EntityInstance : MonoBehaviour
_constantWeaponManagers[data].StopFiring(item);
}
}
var hp = Entity.Hardpoints[item.Position.x, item.Position.y];
if (hp != null && item.Data.SoundBank != 0)
{
var hardpointTransform = EquipmentHardpoints.FirstOrDefault(x => x.name == hp.Transform);
if (hardpointTransform != null)
{
var hardpointGameObject = hardpointTransform.gameObject;
}
}
}
RadiatorMeshes = new Dictionary<Radiator, MeshRenderer>();
Barrels = new Dictionary<HardpointData, Transform[]>();

View file

@ -149,7 +149,6 @@ public static class ShipModShips
instance.InvisibleMaterial = template.Invisible;
instance.Shield = shield;
instance.HullColliders = new[] { surface };
instance.EquipmentHardpoints = plan.Equipment.Select(Node).ToArray();
instance.ThrusterHardpoints = thrusters;
instance.WeaponHardpoints = weapons;
instance.RadiatorHardpoints = radiators;

View file

@ -10,8 +10,6 @@ public sealed class ShipModPlan
public string HullCollider;
public string Shield;
public string Tractor;
// Every hull hardpoint's mount id, in hardpoint order: the anchor node each one is matched to by name.
public string[] Equipment;
// Mount ids whose anchor (role thruster-emitter / radiator-mesh) is the emitter / mesh node itself.
public string[] Thrusters;
public string[] Radiators;
@ -53,7 +51,6 @@ public sealed class ShipModPlan
HullCollider = Only("hull-collider"),
Shield = Only("shield"),
Tractor = Only("tractor"),
Equipment = hardpoints.Select(hardpoint => hardpoint.Transform).ToArray(),
Thrusters = Mounts(HardpointType.Thruster),
Radiators = Mounts(HardpointType.Radiator),
Weapons = weapons

View file

@ -44,13 +44,11 @@ reserved. IDs after a role prefix must be unique across the model. Other visual
Muzzle.1 optional additional barrel
HP.Radiator.Port empty; exact name copied to the catalog
RadiatorMesh one mesh; temperature emission surface
HP.Equipment.Reactor empty; exact name copied to the catalog
Pivot.0.-90.90.-80.80.60 optional empty parent of articulated mounts
```
`HP.Equipment` covers sensors, reactor, shield, cargo, and other mounts without a special visual component.
`HP.Weapon` covers ballistic, energy, and launcher mounts; the catalog chooses which. Every `HP.*` becomes an
`EquipmentHardpoints` entry. Thruster, weapon, and radiator roles also get their matching components and arrays.
`HP.Weapon` covers ballistic, energy, and launcher mounts; the catalog chooses which. Only thrusters, weapons and
radiators are visible and get a node; internal hardpoints have none. Each role gets its matching component and array.
`Muzzle.<nonnegative integer>` children are sorted numerically; their transforms become `FiringPoint`. A weapon
mount requires at least one. A `Pivot` name carries `Group.YawMin.YawMax.PitchMin.PitchMax.Speed` as signed
integers in degrees and degrees per second. It gets `ArticulationPoint`; child mounts follow its transform. Pivots are

View file

@ -33,7 +33,6 @@ public sealed class ShipModPlanTests
var plan = Plan(Rig());
Assert.Equal(("map", "collider", "shield", "tractor"), (plan.MapIcon, plan.HullCollider, plan.Shield, plan.Tractor));
Assert.Equal(new[] { "thruster.port", "gun", "fin" }, plan.Equipment);
Assert.Equal(new[] { "thruster.port" }, plan.Thrusters);
Assert.Equal(new[] { "fin" }, plan.Radiators);
var (mount, muzzles) = Assert.Single(plan.Weapons);