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 step | Source | Behavior |
|---|---|---|
| 1 | Exact-star override (SHIP_STATS_S<n>[hull]) | If field present, used directly. No lerp. |
| 2 | s1Val = SHIP_STATS_S1[hull][field] ?? BASELINE_STATS[field] | Anchor at ★1. |
| 3 | s5Val = SHIP_STATS_S5[hull][field] ?? s1Val | If S5 omits the field, S5 defaults to the ★1 value. |
| 4 | lerp(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:
| Pattern | Field count | Shape |
|---|---|---|
| Minimal | 4 fields | hp, shield, weaponDamagePct, plus one hull-specific bump (armor, acceleration, heatCooldown, shieldRegenRate, or fireRatePct) |
| Verbose | ~30–37 fields | The 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_Glyph | hp, shield, weaponDamagePct, shieldRegenRate |
| Backwater_Caiman | hp, shield, weaponDamagePct, armor |
| Backwater_Killer Croc | hp, shield, weaponDamagePct, armor |
| Backwater_Lizard | hp, shield, weaponDamagePct, armor |
| Industria_Bigbot | hp, shield, weaponDamagePct, heatCooldown |
| Industria_Digbot | hp, shield, weaponDamagePct, heatCooldown |
| Prism_Crystal | hp, shield, weaponDamagePct, acceleration |
| Prism_Ruby | hp, shield, weaponDamagePct, acceleration |
| Solaris_Oracle | hp, shield, weaponDamagePct, acceleration |
| Solaris_Princess | hp, shield, weaponDamagePct, acceleration |
| Solaris_Torque | hp, shield, weaponDamagePct, acceleration |
| Solaris_Valet | hp, shield, weaponDamagePct, acceleration |
Hulls authored in verbose form:
| Hull class | Active ★5 deltas (vs ★1) | Phantom-pin field count |
|---|---|---|
| Ancients_Rune | hp, shield, weaponDamagePct, shieldRegenRate | ~33 |
| Backwater_Batwing | hp, shield, weaponDamagePct, armor | ~33 |
| Industria_Caravan | hp, shield, weaponDamagePct | ~33 (heatCooldown listed but matches ★1) |
| Industria_Drillbot | hp, shield, weaponDamagePct | ~33 |
| Industria_Eel | hp, shield, weaponDamagePct | ~33 |
| Industria_Loader | hp, shield, weaponDamagePct | ~33 |
| Industria_Milita | hp, shield, weaponDamagePct | ~33 |
| Industria_Towncar | hp, shield, weaponDamagePct | ~33 |
| Junkrats_Bomber | hp, shield, weaponDamagePct, fireRatePct | ~32 |
| Junkrats_Drifter | hp, shield, weaponDamagePct, fireRatePct | ~32 |
| Junkrats_Marco | hp, shield, weaponDamagePct, fireRatePct | ~32 (shipScale 1 vs ★1 1.5 — real delta) |
| Junkrats_Orca | hp, shield, weaponDamagePct, armor | ~33 |
| Junkrats_Pierre | hp, shield, weaponDamagePct, fireRatePct | ~32 |
| Junkrats_Skewer | hp, shield, weaponDamagePct, fireRatePct | ~32 |
| Junkrats_Stinger | hp, shield, weaponDamagePct, fireRatePct | ~32 |
| Junkrats_Tank | hp, shield, weaponDamagePct, armor | ~33 |
| Prism_Citrine | hp, shield, weaponDamagePct, acceleration | ~32 |
| Prism_Jade | hp, shield, weaponDamagePct, acceleration | ~32 |
| Prism_Pearl | hp, shield, weaponDamagePct, acceleration | ~32 |
| Prism_Shard | hp, shield, weaponDamagePct, acceleration | ~32 |
| Solaris_Armada | hp, shield, weaponDamagePct, acceleration | ~32 |
| Solaris_Cargo | hp, shield, weaponDamagePct, acceleration | ~32 |
| Solaris_Cruiser | hp, shield, weaponDamagePct, acceleration | ~32 |
| Solaris_Flyer | hp, shield, weaponDamagePct, acceleration | ~32 |
| Solaris_Hauler | hp, 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:
| Hull | Star tier | Phantom-pin example | ★1 value | Star-N override value |
|---|---|---|---|---|
| Junkrats_Drifter | ★5 | weaponSlots | 2 | 2 |
| Junkrats_Drifter | ★5 | acceleration | 170 | 170 |
| Junkrats_Drifter | ★5 | drag | 0.42 | 0.42 |
| Junkrats_Drifter | ★4 | shipScale | 1.4 | 1.4 |
| Junkrats_Bomber | ★4 | shipScale | 1.4 | 1.4 |
| Junkrats_Orca | ★4 | shipScale | 1.2 | 1.2 |
| Junkrats_Pierre | ★4 | shipScale | 1.3 | 1.3 |
| Junkrats_Skewer | ★4 | shipScale | 1.3 | 1.3 |
| Junkrats_Stinger | ★4 | shipScale | 2 | 2 |
| Industria_Caravan | ★5 | heatCooldown | 0 | 0 |
| Industria_Drillbot | ★5 | weaponSlots | 2 | 2 |
| Industria_Loader | ★4 | acceleration | 131 | 131 |
| Industria_Loader | ★4 | speed | 81 | 81 |
| Industria_Loader | ★4 | drag | 0.18 | 0.18 |
| Industria_Milita | ★4 | shipScale | 1.35 | 1.35 |
| Prism_Citrine | ★3, ★4, ★5 | acceleration | 511 | 511 |
| Prism_Citrine | ★3, ★4, ★5 | speed | 211 | 211 |
| Prism_Citrine | ★3, ★4, ★5 | turnRate | 0.64 | 0.64 |
| Prism_Jade | ★3, ★4 | shipScale | 1.55 | 1.55 |
| Prism_Pearl | ★3 | turnRate | 1 | 1 |
| Solaris_Cruiser | ★4 | acceleration | 271 | 271 |
| Solaris_Cruiser | ★4 | speed | 196 | 196 |
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.
Recommended authoring shape
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. weaponSlotsbelongs in ★1 (it is constant per hull). Listing it in ★5 with the same value is always a phantom pin.heatCurveis a string field; it lives in the ★1 override exclusively (the lerp engine pulls it froms1Over.heatCurveonly).- 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_Citrinesaturating speed/acceleration/turnRate at ★3 instead of lerping). When the curve is linear, the records should be empty (SHIP_STATS_S2is empty by design).