What this is

Every hull’s ★5 entry in SHIP_STATS_S5 is a Partial<ShipDef>. Star-1 values come from SHIP_STATS_S1; in-between stars are linearly interpolated by getShipDef. The rule the runtime uses for each numeric LERP_FIELDS member at star n:

Lookup stepSourceBehavior
1Exact-star override (SHIP_STATS_S<n>[hull])If field present, used directly. No lerp.
2s1Val = SHIP_STATS_S1[hull][field] ?? BASELINE_STATS[field]Anchor at ★1.
3s5Val = SHIP_STATS_S5[hull][field] ?? s1ValIf S5 omits the field, S5 defaults to the ★1 value.
4lerp(s1Val, s5Val, (n-1)/4)Interpolated.

The consequence: only fields whose ★5 value differs from ★1 need to appear in SHIP_STATS_S5. Any field whose ★5 value equals ★1 has zero runtime effect whether or not it is listed.

The two patterns

Two authoring shapes exist in SHIP_STATS_S5, both legal, both producing identical getShipDef output:

PatternField countShape
Minimal4 fieldshp, shield, weaponDamagePct, plus one hull-specific bump (armor, acceleration, heatCooldown, shieldRegenRate, or fireRatePct)
Verbose~30–37 fieldsThe 4 minimal fields followed by every other LERP_FIELDS member re-asserted at its ★1 value, plus heatCurve

Hulls authored in minimal form:

Hull class★5 fields
Ancients_Glyphhp, shield, weaponDamagePct, shieldRegenRate
Backwater_Caimanhp, shield, weaponDamagePct, armor
Backwater_Killer Crochp, shield, weaponDamagePct, armor
Backwater_Lizardhp, shield, weaponDamagePct, armor
Industria_Bigbothp, shield, weaponDamagePct, heatCooldown
Industria_Digbothp, shield, weaponDamagePct, heatCooldown
Prism_Crystalhp, shield, weaponDamagePct, acceleration
Prism_Rubyhp, shield, weaponDamagePct, acceleration
Solaris_Oraclehp, shield, weaponDamagePct, acceleration
Solaris_Princesshp, shield, weaponDamagePct, acceleration
Solaris_Torquehp, shield, weaponDamagePct, acceleration
Solaris_Valethp, shield, weaponDamagePct, acceleration

Hulls authored in verbose form:

Hull classActive ★5 deltas (vs ★1)Phantom-pin field count
Ancients_Runehp, shield, weaponDamagePct, shieldRegenRate~33
Backwater_Batwinghp, shield, weaponDamagePct, armor~33
Industria_Caravanhp, shield, weaponDamagePct~33 (heatCooldown listed but matches ★1)
Industria_Drillbothp, shield, weaponDamagePct~33
Industria_Eelhp, shield, weaponDamagePct~33
Industria_Loaderhp, shield, weaponDamagePct~33
Industria_Militahp, shield, weaponDamagePct~33
Industria_Towncarhp, shield, weaponDamagePct~33
Junkrats_Bomberhp, shield, weaponDamagePct, fireRatePct~32
Junkrats_Drifterhp, shield, weaponDamagePct, fireRatePct~32
Junkrats_Marcohp, shield, weaponDamagePct, fireRatePct~32 (shipScale 1 vs ★1 1.5 — real delta)
Junkrats_Orcahp, shield, weaponDamagePct, armor~33
Junkrats_Pierrehp, shield, weaponDamagePct, fireRatePct~32
Junkrats_Skewerhp, shield, weaponDamagePct, fireRatePct~32
Junkrats_Stingerhp, shield, weaponDamagePct, fireRatePct~32
Junkrats_Tankhp, shield, weaponDamagePct, armor~33
Prism_Citrinehp, shield, weaponDamagePct, acceleration~32
Prism_Jadehp, shield, weaponDamagePct, acceleration~32
Prism_Pearlhp, shield, weaponDamagePct, acceleration~32
Prism_Shardhp, shield, weaponDamagePct, acceleration~32
Solaris_Armadahp, shield, weaponDamagePct, acceleration~32
Solaris_Cargohp, shield, weaponDamagePct, acceleration~32
Solaris_Cruiserhp, shield, weaponDamagePct, acceleration~32
Solaris_Flyerhp, shield, weaponDamagePct, acceleration~32
Solaris_Haulerhp, shield, weaponDamagePct, acceleration~32

Both forms produce identical getShipDef output at every star level for the same hull.

Phantom pins

A phantom pin is a field present in a star-N override whose value is identical to the corresponding ★1 value (or the baseline value when ★1 omits the field). It is a no-op: removing it changes nothing at runtime.

Phantom pins are concentrated in the Junkrats hulls and in the Industria/Prism/Solaris verbose ★5 entries. Representative cases:

HullStar tierPhantom-pin example★1 valueStar-N override value
Junkrats_Drifter★5weaponSlots22
Junkrats_Drifter★5acceleration170170
Junkrats_Drifter★5drag0.420.42
Junkrats_Drifter★4shipScale1.41.4
Junkrats_Bomber★4shipScale1.41.4
Junkrats_Orca★4shipScale1.21.2
Junkrats_Pierre★4shipScale1.31.3
Junkrats_Skewer★4shipScale1.31.3
Junkrats_Stinger★4shipScale22
Industria_Caravan★5heatCooldown00
Industria_Drillbot★5weaponSlots22
Industria_Loader★4acceleration131131
Industria_Loader★4speed8181
Industria_Loader★4drag0.180.18
Industria_Milita★4shipScale1.351.35
Prism_Citrine★3, ★4, ★5acceleration511511
Prism_Citrine★3, ★4, ★5speed211211
Prism_Citrine★3, ★4, ★5turnRate0.640.64
Prism_Jade★3, ★4shipScale1.551.55
Prism_Pearl★3turnRate11
Solaris_Cruiser★4acceleration271271
Solaris_Cruiser★4speed196196

For Prism_Pearl at ★4, the override sets turnRate: 0.14 (changed from ★1’s 1) — that one is a real delta, not a phantom pin.

heatCurve re-assertion in verbose ★5 entries is not a phantom pin under the lerp rules — it lives in the non-numeric branch of getShipDef — but it is still a no-op when the value matches the ★1 entry, which is the case for every verbose entry observed.

Minimal is the authoring contract. Verbose entries are legacy noise.

Specifically:

  • A ★5 override should list only fields whose ★5 value differs from ★1.
  • The four-field shape { hp, shield, weaponDamagePct, <one_other> } is sufficient for every existing hull. New hulls should follow it.
  • weaponSlots belongs in ★1 (it is constant per hull). Listing it in ★5 with the same value is always a phantom pin.
  • heatCurve is a string field; it lives in the ★1 override exclusively (the lerp engine pulls it from s1Over.heatCurve only).
  • Phantom pins should be deleted on touch. They confuse diffs, hide intent, and make per-star tables in tooling generate spurious rows.
  • The ★3 and ★4 records exist for non-linear curves (e.g. Prism_Citrine saturating speed/acceleration/turnRate at ★3 instead of lerping). When the curve is linear, the records should be empty (SHIP_STATS_S2 is empty by design).