@elite-dangerous-almanac/core 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/PROVENANCE/i18n/SOURCES.md +2 -2
  2. package/PROVENANCE/ships/SOURCES.md +34 -9
  3. package/README.md +54 -40
  4. package/dist/astro/galaxy-grid.js +1 -1
  5. package/dist/astro/index.d.ts +1 -1
  6. package/dist/astro/index.js +1 -1
  7. package/dist/astro/naming-region-origins.d.ts +1 -1
  8. package/dist/astro/naming-region-origins.js +1 -1
  9. package/dist/astro/nebulae.d.ts +2 -2
  10. package/dist/astro/permit-locks.d.ts +2 -1
  11. package/dist/astro/permit-locks.js +1 -1
  12. package/dist/astro/procedural-system.d.ts +2 -1
  13. package/dist/astro/procedural-system.js +1 -1
  14. package/dist/astro/sector-name.d.ts +2 -1
  15. package/dist/astro/sector-name.js +1 -1
  16. package/dist/astro/system-address.d.ts +1 -1
  17. package/dist/astro/system-name.d.ts +14 -9
  18. package/dist/astro/system-name.js +1 -1
  19. package/dist/chunk-2W5MW376.js +1 -0
  20. package/dist/chunk-2W5MW376.js.map +1 -0
  21. package/dist/chunk-37JIMWLK.js +1 -0
  22. package/dist/chunk-37JIMWLK.js.map +1 -0
  23. package/dist/chunk-4CBMQULR.js +1 -0
  24. package/dist/{chunk-4ZGUSHTP.js.map → chunk-4CBMQULR.js.map} +1 -1
  25. package/dist/{chunk-6EUVIZAS.js → chunk-4QYTVSNR.js} +1 -1
  26. package/dist/chunk-5VWACBTN.js +1 -0
  27. package/dist/chunk-5VWACBTN.js.map +1 -0
  28. package/dist/{chunk-EKQCVCMK.js → chunk-5ZVYRX6Z.js} +1 -1
  29. package/dist/{chunk-EKQCVCMK.js.map → chunk-5ZVYRX6Z.js.map} +1 -1
  30. package/dist/chunk-65FZECUX.js +1 -0
  31. package/dist/chunk-65FZECUX.js.map +1 -0
  32. package/dist/chunk-6BFQIYGZ.js +1 -0
  33. package/dist/chunk-6BFQIYGZ.js.map +1 -0
  34. package/dist/chunk-6GJHUWMO.js +1 -0
  35. package/dist/chunk-6GJHUWMO.js.map +1 -0
  36. package/dist/chunk-6HA2YIKW.js +1 -0
  37. package/dist/chunk-6HA2YIKW.js.map +1 -0
  38. package/dist/{chunk-7EE5VIHR.js → chunk-6HMIF2RJ.js} +1 -1
  39. package/dist/chunk-6S36AVA4.js +1 -0
  40. package/dist/chunk-6S36AVA4.js.map +1 -0
  41. package/dist/{chunk-WNGQ3IAM.js → chunk-6UNAD74Z.js} +1 -1
  42. package/dist/{chunk-WNGQ3IAM.js.map → chunk-6UNAD74Z.js.map} +1 -1
  43. package/dist/chunk-6WTUY6ZX.js +1 -0
  44. package/dist/chunk-6WTUY6ZX.js.map +1 -0
  45. package/dist/{chunk-JPBMRJFC.js → chunk-77I24V6B.js} +1 -1
  46. package/dist/chunk-77I24V6B.js.map +1 -0
  47. package/dist/chunk-77TT4K53.js +1 -0
  48. package/dist/chunk-7ATBEOVC.js +1 -0
  49. package/dist/chunk-7ATBEOVC.js.map +1 -0
  50. package/dist/chunk-A4QKXXZD.js +1 -0
  51. package/dist/chunk-A4QKXXZD.js.map +1 -0
  52. package/dist/{chunk-6EPYVBAX.js → chunk-A6OINYNP.js} +1 -1
  53. package/dist/chunk-AAE4KZ2V.js +1 -0
  54. package/dist/chunk-AAE4KZ2V.js.map +1 -0
  55. package/dist/{chunk-Q5XOGATC.js → chunk-AIEC3LBT.js} +1 -1
  56. package/dist/{chunk-QY3L5BKN.js → chunk-APEG4ZER.js} +1 -1
  57. package/dist/{chunk-Q3QCLNVG.js → chunk-AYAKU4YA.js} +1 -1
  58. package/dist/chunk-AYAKU4YA.js.map +1 -0
  59. package/dist/chunk-B7EYTZRK.js +1 -0
  60. package/dist/chunk-B7EYTZRK.js.map +1 -0
  61. package/dist/{chunk-EBAHNZ36.js → chunk-CIULJH3G.js} +1 -1
  62. package/dist/{chunk-EBAHNZ36.js.map → chunk-CIULJH3G.js.map} +1 -1
  63. package/dist/{chunk-SNADUQSJ.js → chunk-CJK2IBI2.js} +1 -1
  64. package/dist/{chunk-SNADUQSJ.js.map → chunk-CJK2IBI2.js.map} +1 -1
  65. package/dist/{chunk-DYPOL3CI.js → chunk-CJQ37JII.js} +1 -1
  66. package/dist/chunk-CJQ37JII.js.map +1 -0
  67. package/dist/{chunk-Z6XMXMVI.js → chunk-DBDCOHI4.js} +1 -1
  68. package/dist/chunk-DBDCOHI4.js.map +1 -0
  69. package/dist/{chunk-AFVYCQWL.js → chunk-DCB4COCL.js} +1 -1
  70. package/dist/{chunk-AFVYCQWL.js.map → chunk-DCB4COCL.js.map} +1 -1
  71. package/dist/{chunk-CJWFY6OX.js → chunk-DSMLYA2M.js} +1 -1
  72. package/dist/{chunk-CJWFY6OX.js.map → chunk-DSMLYA2M.js.map} +1 -1
  73. package/dist/{chunk-UXBCLFGU.js → chunk-DWRNXUAX.js} +1 -1
  74. package/dist/{chunk-UXBCLFGU.js.map → chunk-DWRNXUAX.js.map} +1 -1
  75. package/dist/chunk-F44QX47A.js +1 -0
  76. package/dist/chunk-F44QX47A.js.map +1 -0
  77. package/dist/{chunk-EVJ32UF3.js → chunk-FA5JVXVG.js} +1 -1
  78. package/dist/{chunk-EVJ32UF3.js.map → chunk-FA5JVXVG.js.map} +1 -1
  79. package/dist/{chunk-4S6WB7QR.js → chunk-FF2X4K5P.js} +1 -1
  80. package/dist/chunk-FF2X4K5P.js.map +1 -0
  81. package/dist/chunk-GGQMIQPC.js +1 -0
  82. package/dist/chunk-GGQMIQPC.js.map +1 -0
  83. package/dist/{chunk-QTZPA2FR.js → chunk-GLWPSVFV.js} +1 -1
  84. package/dist/chunk-GLWPSVFV.js.map +1 -0
  85. package/dist/{chunk-EYGVDJ2I.js → chunk-GMTAFUAG.js} +1 -1
  86. package/dist/{chunk-Z43DN4PY.js → chunk-GTEHRZPN.js} +1 -1
  87. package/dist/{chunk-Z43DN4PY.js.map → chunk-GTEHRZPN.js.map} +1 -1
  88. package/dist/chunk-H47K3UWG.js +1 -0
  89. package/dist/chunk-H47K3UWG.js.map +1 -0
  90. package/dist/{chunk-PA5EITW5.js → chunk-HUYYGTRM.js} +1 -1
  91. package/dist/{chunk-PA5EITW5.js.map → chunk-HUYYGTRM.js.map} +1 -1
  92. package/dist/{chunk-HKXXFIRI.js → chunk-ITKZKREP.js} +1 -1
  93. package/dist/chunk-IWFQ4ZKF.js +1 -0
  94. package/dist/chunk-IWFQ4ZKF.js.map +1 -0
  95. package/dist/{chunk-Y6UZWNP4.js → chunk-J7BM5NGK.js} +1 -1
  96. package/dist/{chunk-Y6UZWNP4.js.map → chunk-J7BM5NGK.js.map} +1 -1
  97. package/dist/chunk-JET673E5.js +1 -0
  98. package/dist/chunk-JET673E5.js.map +1 -0
  99. package/dist/chunk-JMAXSXXQ.js +1 -0
  100. package/dist/chunk-JMAXSXXQ.js.map +1 -0
  101. package/dist/chunk-JO52LYO3.js +1 -0
  102. package/dist/chunk-M2TCZKRT.js +1 -0
  103. package/dist/chunk-M2TCZKRT.js.map +1 -0
  104. package/dist/chunk-M4FZRDAH.js +1 -0
  105. package/dist/chunk-M4FZRDAH.js.map +1 -0
  106. package/dist/{chunk-JWWYAMGD.js → chunk-MGRDB2BZ.js} +1 -1
  107. package/dist/{chunk-JWWYAMGD.js.map → chunk-MGRDB2BZ.js.map} +1 -1
  108. package/dist/{chunk-MKGJPVPS.js → chunk-MRTTQYT5.js} +1 -1
  109. package/dist/{chunk-MKGJPVPS.js.map → chunk-MRTTQYT5.js.map} +1 -1
  110. package/dist/chunk-MTJ4MS7W.js +1 -0
  111. package/dist/chunk-MTJ4MS7W.js.map +1 -0
  112. package/dist/chunk-NIURLW2F.js +1 -0
  113. package/dist/chunk-NIURLW2F.js.map +1 -0
  114. package/dist/chunk-OJC6AUL7.js +1 -0
  115. package/dist/chunk-OJC6AUL7.js.map +1 -0
  116. package/dist/chunk-OLKNYGGT.js +1 -0
  117. package/dist/chunk-OLKNYGGT.js.map +1 -0
  118. package/dist/chunk-PL75RTA2.js +1 -0
  119. package/dist/{chunk-2VPVZELZ.js.map → chunk-PL75RTA2.js.map} +1 -1
  120. package/dist/chunk-Q5ALV3DI.js +1 -0
  121. package/dist/chunk-Q5ALV3DI.js.map +1 -0
  122. package/dist/{chunk-SQS7672E.js → chunk-Q6YDPREF.js} +1 -1
  123. package/dist/chunk-SG6YLUQ4.js +1 -0
  124. package/dist/chunk-SG6YLUQ4.js.map +1 -0
  125. package/dist/{chunk-OVUHQEH5.js → chunk-T4MKUKYS.js} +1 -1
  126. package/dist/{chunk-OVUHQEH5.js.map → chunk-T4MKUKYS.js.map} +1 -1
  127. package/dist/{chunk-J42ZXLBL.js → chunk-T62GO4AA.js} +1 -1
  128. package/dist/chunk-T62GO4AA.js.map +1 -0
  129. package/dist/{chunk-E3BRVGJJ.js → chunk-TBZJIQ5P.js} +1 -1
  130. package/dist/{chunk-E3BRVGJJ.js.map → chunk-TBZJIQ5P.js.map} +1 -1
  131. package/dist/chunk-VEEBJFJ7.js +1 -0
  132. package/dist/chunk-VEEBJFJ7.js.map +1 -0
  133. package/dist/{chunk-L747RVPO.js → chunk-VQJXRPYI.js} +1 -1
  134. package/dist/{chunk-L747RVPO.js.map → chunk-VQJXRPYI.js.map} +1 -1
  135. package/dist/chunk-VTMCSCXV.js +1 -0
  136. package/dist/chunk-VTMCSCXV.js.map +1 -0
  137. package/dist/chunk-XQJERZUS.js +1 -0
  138. package/dist/chunk-XQJERZUS.js.map +1 -0
  139. package/dist/commodities/commodities-all.js +1 -1
  140. package/dist/commodities/commodities-rare.js +1 -1
  141. package/dist/commodities/commodities-standard.js +1 -1
  142. package/dist/commodities/commodities.d.ts +20 -10
  143. package/dist/commodities/commodities.js +1 -1
  144. package/dist/commodities/index.d.ts +15 -4
  145. package/dist/commodities/index.js +1 -1
  146. package/dist/equipment/index.js +1 -1
  147. package/dist/equipment/modification-costs.d.ts +2 -1
  148. package/dist/equipment/modification-costs.js.map +1 -1
  149. package/dist/equipment/modification-journal.js +1 -1
  150. package/dist/equipment/modifications.d.ts +2 -1
  151. package/dist/equipment/modifications.js +1 -1
  152. package/dist/equipment/suits.d.ts +6 -3
  153. package/dist/equipment/suits.js +1 -1
  154. package/dist/equipment/upgrade-costs.d.ts +8 -4
  155. package/dist/equipment/upgrade-costs.js +1 -1
  156. package/dist/equipment/upgrade-costs.js.map +1 -1
  157. package/dist/equipment/weapons.d.ts +13 -5
  158. package/dist/equipment/weapons.js +1 -1
  159. package/dist/i18n/blueprints.d.ts +6 -6
  160. package/dist/i18n/blueprints.js +1 -1
  161. package/dist/i18n/diagnostics.d.ts +1 -13
  162. package/dist/i18n/engineering-groups.d.ts +1 -0
  163. package/dist/i18n/engineering-groups.js +1 -1
  164. package/dist/i18n/experimental-effect-descriptions.d.ts +4 -3
  165. package/dist/i18n/experimental-effect-descriptions.js +1 -1
  166. package/dist/i18n/experimental-effects.d.ts +5 -4
  167. package/dist/i18n/experimental-effects.js +1 -1
  168. package/dist/i18n/index.d.ts +1 -13
  169. package/dist/i18n/index.js +1 -1
  170. package/dist/i18n/pre-engineered.d.ts +7 -6
  171. package/dist/i18n/pre-engineered.js +1 -1
  172. package/dist/materials/index.d.ts +16 -4
  173. package/dist/materials/index.js +1 -1
  174. package/dist/materials/materials-all.js +1 -1
  175. package/dist/materials/materials-encoded.js +1 -1
  176. package/dist/materials/materials-manufactured.js +1 -1
  177. package/dist/materials/materials-raw.js +1 -1
  178. package/dist/materials/materials.d.ts +36 -19
  179. package/dist/materials/materials.js +1 -1
  180. package/dist/materials/micro-resources.d.ts +18 -7
  181. package/dist/materials/micro-resources.js +1 -1
  182. package/dist/ships/armour.d.ts +20 -4
  183. package/dist/ships/armour.js +1 -1
  184. package/dist/ships/blueprint-costs.d.ts +20 -17
  185. package/dist/ships/blueprint-costs.js +1 -1
  186. package/dist/ships/blueprint-journal.d.ts +9 -8
  187. package/dist/ships/blueprint-journal.js +1 -1
  188. package/dist/ships/blueprints.d.ts +14 -12
  189. package/dist/ships/blueprints.js +1 -1
  190. package/dist/ships/build-metrics.d.ts +1061 -0
  191. package/dist/ships/build-metrics.js +1 -0
  192. package/dist/ships/build-metrics.js.map +1 -0
  193. package/dist/ships/distributor.d.ts +10 -3
  194. package/dist/ships/distributor.js +1 -1
  195. package/dist/ships/engineering-options.d.ts +8 -6
  196. package/dist/ships/engineering-options.js +1 -1
  197. package/dist/ships/engineering.d.ts +2 -16
  198. package/dist/ships/engineering.js +1 -1
  199. package/dist/ships/experimental-effect-costs.d.ts +5 -5
  200. package/dist/ships/experimental-effect-costs.js +1 -1
  201. package/dist/ships/experimental-effects.d.ts +9 -8
  202. package/dist/ships/experimental-effects.js +1 -1
  203. package/dist/ships/heat.d.ts +7 -2
  204. package/dist/ships/heat.js +1 -1
  205. package/dist/ships/index.d.ts +35 -23
  206. package/dist/ships/index.js +1 -1
  207. package/dist/ships/jump-range.d.ts +10 -3
  208. package/dist/ships/jump-range.js +1 -1
  209. package/dist/ships/loadout-calculations.d.ts +1 -1
  210. package/dist/ships/loadout-calculations.js +1 -1
  211. package/dist/ships/loadout-validation.d.ts +111 -5
  212. package/dist/ships/loadout-validation.js +1 -1
  213. package/dist/ships/mobility-capacitor.d.ts +90 -0
  214. package/dist/ships/mobility-capacitor.js +1 -0
  215. package/dist/ships/mobility-capacitor.js.map +1 -0
  216. package/dist/ships/mobility.d.ts +29 -17
  217. package/dist/ships/mobility.js +1 -1
  218. package/dist/ships/modules-all.js +1 -1
  219. package/dist/ships/modules-core.js +1 -1
  220. package/dist/ships/modules-internal.js +1 -1
  221. package/dist/ships/modules.d.ts +21 -1
  222. package/dist/ships/modules.js +1 -1
  223. package/dist/ships/power.d.ts +12 -5
  224. package/dist/ships/power.js +1 -1
  225. package/dist/ships/pre-engineered-stats.d.ts +20 -13
  226. package/dist/ships/pre-engineered-stats.js +1 -1
  227. package/dist/ships/pre-engineered.d.ts +6 -6
  228. package/dist/ships/pre-engineered.js +1 -1
  229. package/dist/ships/resistances.d.ts +2 -2
  230. package/dist/ships/resistances.js +1 -1
  231. package/dist/ships/shield-capacitor.d.ts +133 -0
  232. package/dist/ships/shield-capacitor.js +1 -0
  233. package/dist/ships/shield-capacitor.js.map +1 -0
  234. package/dist/ships/shield-recovery.d.ts +16 -2
  235. package/dist/ships/shield-recovery.js +1 -1
  236. package/dist/ships/shields.d.ts +86 -28
  237. package/dist/ships/shields.js +1 -1
  238. package/dist/ships/ship-loadout.d.ts +176 -815
  239. package/dist/ships/ship-loadout.js +1 -1
  240. package/dist/ships/ships.d.ts +7 -6
  241. package/dist/ships/ships.js +1 -1
  242. package/dist/ships/slef.d.ts +5 -2
  243. package/dist/ships/slef.js +1 -1
  244. package/dist/ships/source-purchase.d.ts +2 -1
  245. package/dist/ships/source-purchase.js +1 -1
  246. package/dist/ships/weapons-capacitor.d.ts +10 -3
  247. package/dist/ships/weapons-capacitor.js +1 -1
  248. package/dist/ships/weapons.d.ts +22 -6
  249. package/dist/ships/weapons.js +1 -1
  250. package/dist/{system-address-DYsN1qOT.d.ts → system-address-cyJaeZIS.d.ts} +2 -1
  251. package/package.json +17 -2
  252. package/dist/chunk-2FHHMYDC.js +0 -1
  253. package/dist/chunk-2FHHMYDC.js.map +0 -1
  254. package/dist/chunk-2TW4ZBI3.js +0 -1
  255. package/dist/chunk-2TW4ZBI3.js.map +0 -1
  256. package/dist/chunk-2VPVZELZ.js +0 -1
  257. package/dist/chunk-3BM5BHI3.js +0 -1
  258. package/dist/chunk-3BM5BHI3.js.map +0 -1
  259. package/dist/chunk-4S6WB7QR.js.map +0 -1
  260. package/dist/chunk-4ZGUSHTP.js +0 -1
  261. package/dist/chunk-6BO5JJNI.js +0 -1
  262. package/dist/chunk-6BO5JJNI.js.map +0 -1
  263. package/dist/chunk-B4ZSTOM4.js +0 -1
  264. package/dist/chunk-B4ZSTOM4.js.map +0 -1
  265. package/dist/chunk-CX7B67OI.js +0 -1
  266. package/dist/chunk-CX7B67OI.js.map +0 -1
  267. package/dist/chunk-DVL5RBKX.js +0 -1
  268. package/dist/chunk-DVL5RBKX.js.map +0 -1
  269. package/dist/chunk-DYPOL3CI.js.map +0 -1
  270. package/dist/chunk-FGXWVFN6.js +0 -1
  271. package/dist/chunk-FGXWVFN6.js.map +0 -1
  272. package/dist/chunk-FMPRCYKY.js +0 -1
  273. package/dist/chunk-FMPRCYKY.js.map +0 -1
  274. package/dist/chunk-G3265B27.js +0 -1
  275. package/dist/chunk-G3265B27.js.map +0 -1
  276. package/dist/chunk-GQ5GX5TB.js +0 -1
  277. package/dist/chunk-GQ5GX5TB.js.map +0 -1
  278. package/dist/chunk-IOJXLDJN.js +0 -1
  279. package/dist/chunk-IOJXLDJN.js.map +0 -1
  280. package/dist/chunk-IYU4WLFM.js +0 -1
  281. package/dist/chunk-IYU4WLFM.js.map +0 -1
  282. package/dist/chunk-J42ZXLBL.js.map +0 -1
  283. package/dist/chunk-JPBMRJFC.js.map +0 -1
  284. package/dist/chunk-JQG4C67D.js +0 -1
  285. package/dist/chunk-LJI7VXJD.js +0 -1
  286. package/dist/chunk-LJI7VXJD.js.map +0 -1
  287. package/dist/chunk-OCND33TO.js +0 -1
  288. package/dist/chunk-OCND33TO.js.map +0 -1
  289. package/dist/chunk-P2QOL63K.js +0 -1
  290. package/dist/chunk-P2QOL63K.js.map +0 -1
  291. package/dist/chunk-Q3QCLNVG.js.map +0 -1
  292. package/dist/chunk-QTZPA2FR.js.map +0 -1
  293. package/dist/chunk-RLUS76LZ.js +0 -1
  294. package/dist/chunk-RLUS76LZ.js.map +0 -1
  295. package/dist/chunk-TEOJCBSG.js +0 -1
  296. package/dist/chunk-TEOJCBSG.js.map +0 -1
  297. package/dist/chunk-TMMGS6TC.js +0 -1
  298. package/dist/chunk-TMMGS6TC.js.map +0 -1
  299. package/dist/chunk-V4C6FIE2.js +0 -1
  300. package/dist/chunk-VZZUNSDF.js +0 -1
  301. package/dist/chunk-VZZUNSDF.js.map +0 -1
  302. package/dist/chunk-W6EPHHTS.js +0 -1
  303. package/dist/chunk-W6EPHHTS.js.map +0 -1
  304. package/dist/chunk-Z6XMXMVI.js.map +0 -1
  305. package/dist/chunk-ZNCXENNB.js +0 -1
  306. package/dist/chunk-ZNCXENNB.js.map +0 -1
  307. /package/dist/{chunk-6EUVIZAS.js.map → chunk-4QYTVSNR.js.map} +0 -0
  308. /package/dist/{chunk-7EE5VIHR.js.map → chunk-6HMIF2RJ.js.map} +0 -0
  309. /package/dist/{chunk-JQG4C67D.js.map → chunk-77TT4K53.js.map} +0 -0
  310. /package/dist/{chunk-6EPYVBAX.js.map → chunk-A6OINYNP.js.map} +0 -0
  311. /package/dist/{chunk-Q5XOGATC.js.map → chunk-AIEC3LBT.js.map} +0 -0
  312. /package/dist/{chunk-QY3L5BKN.js.map → chunk-APEG4ZER.js.map} +0 -0
  313. /package/dist/{chunk-EYGVDJ2I.js.map → chunk-GMTAFUAG.js.map} +0 -0
  314. /package/dist/{chunk-HKXXFIRI.js.map → chunk-ITKZKREP.js.map} +0 -0
  315. /package/dist/{chunk-V4C6FIE2.js.map → chunk-JO52LYO3.js.map} +0 -0
  316. /package/dist/{chunk-SQS7672E.js.map → chunk-Q6YDPREF.js.map} +0 -0
@@ -1,25 +1,13 @@
1
1
  import { ModuleEngineering, LoadoutModule, LoadoutEvent, SlefHeader, Slef } from './slef.js';
2
- import { TotalRangeDetails, FrameShiftDriveParams } from './jump-range.js';
3
2
  import { BuildSlot, SlotKind } from './slots.js';
4
- import { EngineeringMaterial } from './engineering.js';
5
- import { OutfittingModule, ProjectileRangeBoundaries } from './modules.js';
6
- import { PowerBudget } from './power.js';
7
- import { HeatMetrics } from './heat.js';
8
- import { ShieldMetrics } from './shields.js';
9
- import { ArmourMetrics } from './armour.js';
10
- import { WeaponMetrics, WeaponTotals } from './weapons.js';
11
- import { AmmunitionCapacity } from './ammunition.js';
12
- import { WeaponsCapacitorMetrics } from './weapons-capacitor.js';
13
- import { DistributorMetrics } from './distributor.js';
14
- import { ThrusterParams, MobilityMetrics } from './mobility.js';
15
- import { ShieldRecovery, CellBankSummary } from './shield-recovery.js';
3
+ import { OutfittingModule } from './modules.js';
16
4
  import { PreEngineeredVariant } from './pre-engineered.js';
5
+ import { AmmunitionCapacity } from './ammunition.js';
17
6
  import { SourcePurchaseRecord } from './source-purchase.js';
18
- import { FuelCapacity, CalculationResult } from './loadout-calculations.js';
7
+ import { FuelCapacity } from './loadout-calculations.js';
19
8
  import { ModuleFitConstraint, LoadoutIssueParams, LoadoutValidation } from './loadout-validation.js';
20
9
  import './engineering-options.js';
21
10
  import './module-families.js';
22
- import './resistances.js';
23
11
 
24
12
  /**
25
13
  * Immutable fitted-module snapshots returned by {@link ShipLoadout}.
@@ -172,10 +160,13 @@ type LoadoutSlot = BuildSlot & {
172
160
  * A change made while normalizing a journal or SLEF loadout against the catalogues.
173
161
  *
174
162
  * @remarks
175
- * `ShipLoadout.fromLoadout` states the rule these outcomes report: a removable mount is
163
+ * `ShipLoadout.fromLoadout` states the rules these outcomes report: a removable mount is
176
164
  * `emptied` of an article the catalogue cannot resolve, and a fixed mount is `defaulted`
177
165
  * to the hull's stock article whenever the source left none it can hold — with a `null`
178
- * `sourceSymbol` when the source named nothing there at all.
166
+ * `sourceSymbol` when the source named nothing there at all. A module whose source stated
167
+ * a recipe and no `Modifiers` reports `unresolvedEngineering` when neither a craftable
168
+ * recipe nor a catalogued article answers to what it named, because that module alone
169
+ * keeps the figures of an unengineered one.
179
170
  *
180
171
  * @example
181
172
  * ```ts
@@ -206,11 +197,24 @@ type LoadoutImportOutcome = {
206
197
  readonly sourceSymbol: string | null;
207
198
  /** Stock module identity installed in the mount. */
208
199
  readonly replacementSymbol: string;
200
+ } | {
201
+ /**
202
+ * The source stated engineering with no `Modifiers`, and what it named could
203
+ * not be rolled — an unknown or unoffered blueprint or experimental effect, a
204
+ * grade or quality outside the recipe, or a base stat the catalogues do not
205
+ * carry — so this module's figures are the unengineered ones.
206
+ */
207
+ readonly action: 'unresolvedEngineering';
208
+ /** Exact slot spelling used by the imported build. */
209
+ readonly slot: string;
210
+ /** Module identity the recipe was stated for. */
211
+ readonly sourceSymbol: string;
212
+ /** Recipe the source named, in its own spelling. */
213
+ readonly blueprintSymbol: string;
209
214
  };
210
215
 
211
216
  /**
212
- * {@link ShipLoadout} — a mutable fitted-ship model that both **answers questions**
213
- * about a build and **edits** it.
217
+ * {@link ShipLoadout} — a mutable fitted-ship model that **holds and edits** a build.
214
218
  *
215
219
  * Load one from a SLEF export or a journal `Loadout` event, or start from a
216
220
  * {@link ShipLoadout.default | default} or {@link ShipLoadout.empty | empty} hull, then
@@ -218,9 +222,15 @@ type LoadoutImportOutcome = {
218
222
  * build in place and return `this`; everything a query returns is a deeply frozen
219
223
  * snapshot, so query again after an edit rather than re-reading an earlier value.
220
224
  *
221
- * **Slot keys are matched case-insensitively.** Frontier writes `FrameShiftDrive`, a
222
- * SLEF producer may write `frameshiftdrive`, and both name the same mount. A build's own
223
- * spelling is never rewritten, so re-exporting an import returns it untouched.
225
+ * **What a build *calculates* lives next door.** Jump range, mass, cost, power, heat,
226
+ * mobility, shields, armour and firepower are on
227
+ * {@link ships!BuildMetrics | BuildMetrics}, over the same build:
228
+ * `BuildMetrics.of(build).maxJumpRange()`.
229
+ *
230
+ * **Slot keys are matched case-insensitively, with surrounding whitespace ignored.**
231
+ * Frontier writes `FrameShiftDrive`, a SLEF producer may write `frameshiftdrive`, and
232
+ * both name the same mount. A build's own spelling is never rewritten, so re-exporting
233
+ * an import returns it untouched.
224
234
  *
225
235
  * @remarks
226
236
  * This is the batteries-included ship facade: it carries the complete ship, module,
@@ -231,11 +241,12 @@ type LoadoutImportOutcome = {
231
241
  * ```ts
232
242
  * declare const slefJsonString: string;
233
243
  *
244
+ * import { BuildMetrics } from '@elite-dangerous-almanac/core/ships/build-metrics';
234
245
  * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
235
246
  *
236
247
  * // Read a build:
237
248
  * const build = ShipLoadout.fromSlef(slefJsonString);
238
- * build.maxJumpRange(); // -> 89.41 (best single jump, one jump's fuel, no cargo)
249
+ * BuildMetrics.of(build).maxJumpRange(); // -> 89.41 (best single jump, one jump's fuel, no cargo)
239
250
  *
240
251
  * // Assemble one:
241
252
  * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
@@ -330,13 +341,6 @@ type FixedMountRepairResult = {
330
341
  /** Stable refusal reason. */
331
342
  readonly reason: 'notFixedMount';
332
343
  };
333
- /** Optional mass overrides for a single calculation. */
334
- interface JumpOptions {
335
- /** Finite non-negative fuel load, in tonnes. Defaults to the full main tank. */
336
- readonly fuel?: number;
337
- /** Finite non-negative cargo load, in tonnes. Defaults to `0` (unladen). */
338
- readonly cargo?: number;
339
- }
340
344
  /** Options for {@link ShipLoadout.applyBlueprint}. */
341
345
  interface ApplyBlueprintOptions {
342
346
  /** The blueprint grade, `1`–`5`. */
@@ -347,8 +351,8 @@ interface ApplyBlueprintOptions {
347
351
  * reconstructed from its single reported quality; import its stated modifiers instead.
348
352
  */
349
353
  readonly quality?: number;
350
- /** The experimental (special) effect's Frontier `fdname`, if any. */
351
- readonly experimental?: string;
354
+ /** The experimental (special) effect's Frontier symbol, if any. */
355
+ readonly experimentalEffectSymbol?: string;
352
356
  }
353
357
  /** Stable reason {@link ShipLoadout.setExperimentalEffect} cannot perform an edit. */
354
358
  type ExperimentalEffectMutationCode = 'emptySlot' | 'notEngineered' | 'unknownExperimentalEffect' | 'unsupportedExperimentalEffect' | 'finalArticle' | 'unsupportedEngineering' | 'unidentifiedPreEngineeredVariant' | 'unresolvedModifiers';
@@ -356,17 +360,17 @@ type ExperimentalEffectMutationCode = 'emptySlot' | 'notEngineered' | 'unknownEx
356
360
  interface ExperimentalEffectUpdated {
357
361
  /** Discriminator for an edit that changed the fitted module. */
358
362
  readonly kind: 'updated';
359
- /** Effect id before the edit, or `null` when none was present. */
360
- readonly previousExperimental: string | null;
361
- /** Effect id after the edit, or `null` when it was removed. */
362
- readonly experimental: string | null;
363
+ /** Effect symbol before the edit, or `null` when none was present. */
364
+ readonly previousExperimentalEffectSymbol: string | null;
365
+ /** Effect symbol after the edit, or `null` when it was removed. */
366
+ readonly experimentalEffectSymbol: string | null;
363
367
  }
364
368
  /** Effect-only edit that requested the fitted module's current effect. */
365
369
  interface ExperimentalEffectUnchanged {
366
370
  /** Discriminator for a no-op edit. */
367
371
  readonly kind: 'unchanged';
368
- /** Current effect id, or `null` when none is present. */
369
- readonly experimental: string | null;
372
+ /** Current effect symbol, or `null` when none is present. */
373
+ readonly experimentalEffectSymbol: string | null;
370
374
  }
371
375
  /** Effect-only edit that cannot be performed losslessly. */
372
376
  interface ExperimentalEffectUnsupported {
@@ -391,8 +395,9 @@ interface EngineeringNormalized {
391
395
  /** Discriminator for a successful normalization. */
392
396
  readonly kind: 'normalized';
393
397
  /**
394
- * Quality reported by the fitted module before normalization, in `[0, 1]`. It reads
395
- * `1` where a completed roll stated no modifiers and this call spelled them out.
398
+ * Quality reported by the fitted module before normalization, in `[0, 1]`. A block
399
+ * that stated no modifiers still reports the quality it stated, so this reads `1` for
400
+ * a completed roll whose figures this call was the first to spell out.
396
401
  */
397
402
  readonly previousQuality: number;
398
403
  /** Completed engineering quality. Always `1`. */
@@ -409,223 +414,10 @@ interface EngineeringNormalizationUnsupported {
409
414
  }
410
415
  /** Result of {@link ShipLoadout.completeEngineeringGrade}. */
411
416
  type EngineeringNormalizationResult = EngineeringNormalizationUnchanged | EngineeringNormalized | EngineeringNormalizationUnsupported;
412
- /** Options for the defence figures a build reports. */
413
- interface DefenceOptions {
414
- /**
415
- * Pips to the systems capacitor, `0`–`4`, folded into the shield resistances.
416
- * Defaults to `0` for {@link ShipLoadout.shieldMetrics} and `4` for
417
- * {@link ShipLoadout.shieldRecovery}.
418
- */
419
- readonly systemsPips?: number;
420
- }
421
- /** Optional load and ENG allocation for {@link ShipLoadout.mobilityMetrics}. */
422
- interface MobilityOptions extends JumpOptions {
423
- /** Pips assigned to the engines capacitor, `0`–`4`. Defaults to `4`. */
424
- readonly enginesPips?: number;
425
- }
426
- /** A standard fuel-and-cargo condition shared by jump and mobility views. */
427
- type StandardLoad = 'maximum' | 'unladen' | 'laden';
428
- /** What a {@link StandardLoad} carries, and what the ship weighs carrying it. */
429
- interface StandardLoadInputs {
430
- /** Main-tank fuel carried, in tonnes. */
431
- readonly fuel: number;
432
- /** Cargo carried, in tonnes. */
433
- readonly cargo: number;
434
- /**
435
- * What the ship weighs at this load, in tonnes: {@link ShipLoadout.unladenMass}
436
- * plus `fuel` plus `cargo`.
437
- *
438
- * @remarks
439
- * This is the mass the jump and mobility calculations run on, so it is the figure
440
- * to show beside them rather than one reassembled by the caller. The reserve tank
441
- * is **not** in it: the game's statistics panel counts the reserve in the current
442
- * mass it displays, and neither calculation here does — see
443
- * {@link ShipLoadout.mobilityMetrics}. Add {@link FuelCapacity.reserve} to match the
444
- * panel.
445
- *
446
- * The extra `fuel` and `cargo` are the load a screen labels; the mass is what they
447
- * add up to, and passing the whole value back into {@link ShipLoadout.jumpRange} or
448
- * {@link ShipLoadout.mobilityMetrics} is unaffected by its presence.
449
- */
450
- readonly mass: number;
451
- }
452
- /** Optional WEP allocation for {@link ShipLoadout.weaponsCapacitorMetrics}. */
453
- interface WeaponsOptions {
454
- /** Pips assigned to the weapons capacitor, `0`–`4`. Defaults to `4`. */
455
- readonly weaponsPips?: number;
456
- }
457
- /** Optional SYS, ENG and WEP allocations for {@link ShipLoadout.distributorMetrics}. */
458
- interface DistributorOptions {
459
- /** Pips assigned to the systems capacitor, `0`–`4`. Defaults to `4`. */
460
- readonly systemsPips?: number;
461
- /** Pips assigned to the engines capacitor, `0`–`4`. Defaults to `4`. */
462
- readonly enginesPips?: number;
463
- /** Pips assigned to the weapons capacitor, `0`–`4`. Defaults to `4`. */
464
- readonly weaponsPips?: number;
465
- }
466
- /**
467
- * Retail catalogue credits for an assembled build, as {@link ShipLoadout.buildCost} prices it.
468
- */
469
- interface BuildCredits {
470
- /**
471
- * Priced hull and modules together, in credits.
472
- *
473
- * A Mercenary article is bought with Merc Coin and has no credit price at all, but it
474
- * is counted here at the catalogue list price of the stock module it is built on, and
475
- * again in {@link BuildCost.mercCoins} at what it actually cost. Subtract the stock
476
- * module's price to quote credits a shop would really ask.
477
- */
478
- readonly total: number;
479
- /** Bare hull list price in credits. */
480
- readonly hull: number;
481
- /** Sum of every priced fitted module, in credits. A lower bound when `unpriced` is non-empty. */
482
- readonly modules: number;
483
- /**
484
- * Five percent of `total`, truncated to credits: what insurance bills to rebuild the
485
- * fit at catalogue prices. For what a capture said its own rebuy was, read
486
- * {@link ShipLoadout.rebuy}.
487
- */
488
- readonly rebuy: number;
489
- /** Fitted modules that could not be priced from the catalogue. */
490
- readonly unpriced: readonly {
491
- readonly slot: string;
492
- readonly symbol: string;
493
- }[];
494
- }
495
- /**
496
- * What an assembled build costs to own, in all three currencies the game charges for it.
497
- *
498
- * Every figure prices the **current fit** from the catalogues rather than reporting what a
499
- * capture said was paid; for the latter read {@link ShipLoadout.sourcePurchase}.
500
- */
501
- interface BuildCost {
502
- /** Shop credits for the hull and its fitted modules. */
503
- readonly credits: BuildCredits;
504
- /**
505
- * Merc Coin billed by the build: every Mercenary article's shop price plus every
506
- * blueprint's currency cost, including ordinary engineering-menu recipes that charge it.
507
- * A Mercenary article's blueprint is charged only above the grade it was sold at.
508
- */
509
- readonly mercCoins: number;
510
- /**
511
- * What the build's blueprints and experimental effects consume, one entry per distinct
512
- * material, counts summed across modules.
513
- *
514
- * Pre-engineered articles arrive engineered, so only what a player still has to roll on
515
- * top of one is charged. A fixed reward carries no craft recipe at all and contributes
516
- * nothing, and so does a modification whose recipe the catalogues do not price — a
517
- * capture may name a blueprint or effect no registry lists, and an unpriceable
518
- * modification is silently absent rather than reported the way
519
- * {@link BuildCredits.unpriced} reports an unpriceable module.
520
- */
521
- readonly materials: readonly EngineeringMaterial[];
522
- }
523
- /**
524
- * What an assembled build weighs, broken down the way {@link ShipLoadout.buildMass}
525
- * weighs it. Every figure is in tonnes.
526
- *
527
- * @remarks
528
- * The mass counterpart of {@link BuildCredits}, and the same split: what the bare hull
529
- * contributes, what the fit adds, and the total. `fuel` and `cargo` are the chosen load
530
- * on top of that, so `total` is the mass the jump and mobility calculations run on.
531
- */
532
- interface BuildMass {
533
- /** Bare hull mass — the {@link ships!Ship.hullMass | hullMass} of the hull being flown. */
534
- readonly hull: number;
535
- /**
536
- * Every fitted module's post-engineering mass, summed.
537
- *
538
- * @remarks
539
- * Lightweight blueprints are already folded in, and the cargo hatch weighs nothing.
540
- * A fitted record with no mass at all contributes `0` rather than making the total
541
- * unavailable — mass is the one figure no article can be missing (see
542
- * {@link ShipLoadout.unladenMass}), which is why there is no `unpriced` counterpart
543
- * to {@link BuildCredits.unpriced} here.
544
- */
545
- readonly modules: number;
546
- /**
547
- * The ship with an empty tank and no cargo — {@link ShipLoadout.unladenMass}.
548
- *
549
- * @remarks
550
- * `hull` and `modules` are always computed from the hull record and the current
551
- * fit, while this is the build's own unladen mass, which for an unedited import is
552
- * the figure the **capture** stated. The two agree on anything assembled here; where
553
- * a capture disagrees with the catalogues, this is the one the jump and mobility
554
- * calculations use and the decomposition is what the catalogues say it is made of.
555
- */
556
- readonly unladen: number;
557
- /** Main-tank fuel counted, in tonnes. Defaults to a full main tank. */
558
- readonly fuel: number;
559
- /** Cargo counted, in tonnes. Defaults to an empty hold. */
560
- readonly cargo: number;
561
- /** `unladen + fuel + cargo`: what the ship weighs at the chosen load. */
562
- readonly total: number;
563
- }
564
- /** One fitted weapon and what it does, as {@link ShipLoadout.weaponMetrics} reports it. */
565
- interface FittedWeaponMetrics {
566
- /** The hardpoint's slot key, e.g. `"LargeHardpoint1"`. */
567
- readonly slot: string;
568
- /** The weapon's internal symbol. */
569
- readonly symbol: string;
570
- /** The weapon's display name, e.g. `"Multi-Cannon"`. */
571
- readonly name: string;
572
- /** Whether the weapon is switched on — a disabled weapon is excluded from the totals. */
573
- readonly enabled: boolean;
574
- /** What this weapon does per second, post-engineering. */
575
- readonly metrics: WeaponMetrics;
576
- /**
577
- * How many rounds it holds when fully rearmed, post-engineering — `null` for a laser,
578
- * which carries none. A capacity, not a rearm state: see {@link FittedModule.ammunition}.
579
- */
580
- readonly ammunition: AmmunitionCapacity | null;
581
- /** Maximum effective range in metres, absent when the fitted weapon does not state one. */
582
- readonly maximumRange?: number;
583
- /** Damage-falloff start in metres, absent when the fitted weapon does not state one. */
584
- readonly falloffRange?: number;
585
- /**
586
- * Exact projectile boundary metadata, absent when unavailable. These are not
587
- * effective distances and remain separate from {@link maximumRange} and
588
- * {@link falloffRange}.
589
- */
590
- readonly projectileRange?: ProjectileRangeBoundaries;
591
- /** Armour-piercing rating, absent when unavailable. */
592
- readonly armourPiercing?: number;
593
- }
594
- /** A build's firepower: every fitted weapon, and the totals across the enabled ones. */
595
- interface BuildWeaponMetrics {
596
- /**
597
- * Every fitted weapon in hull slot order. Weapons in unknown or unmapped slots
598
- * follow the known slots in their original source order.
599
- */
600
- readonly weapons: readonly FittedWeaponMetrics[];
601
- /** The additive totals across the **enabled** weapons. */
602
- readonly total: WeaponTotals;
603
- }
604
- /**
605
- * A build's jump ranges at the loads that matter. The three single-jump values and
606
- * each total result's `range` are in light-years.
607
- */
608
- interface JumpRangeSummary {
609
- /**
610
- * Best single jump: no cargo, and only one jump's fuel aboard — the figure the game
611
- * and EDSY label "maximum jump range".
612
- */
613
- readonly max: number;
614
- /** Single jump on a full tank with an empty hold. */
615
- readonly unladen: number;
616
- /** Single jump on a full tank with a full hold. */
617
- readonly laden: number;
618
- /** Summed range and jump count on one jump's fuel, empty hold. */
619
- readonly totalMax: TotalRangeDetails;
620
- /** Summed range and jump count on one full tank, empty hold. */
621
- readonly totalUnladen: TotalRangeDetails;
622
- /** Summed range and jump count on one full tank, full hold. */
623
- readonly totalLaden: TotalRangeDetails;
624
- }
625
417
  /** A blueprint candidate for a module symbol, with its grades and availability route. */
626
418
  interface AvailableBlueprint {
627
- /** The blueprint's Frontier `fdname`, e.g. `"FSD_LongRange"`. */
628
- readonly fdname: string;
419
+ /** The blueprint's Frontier symbol, e.g. `"FSD_LongRange"`. */
420
+ readonly blueprintSymbol: string;
629
421
  /** The grades the blueprint offers, ascending (e.g. `[1, 2, 3, 4, 5]`). */
630
422
  readonly grades: readonly number[];
631
423
  /**
@@ -688,6 +480,46 @@ interface SlefExportOptions extends LoadoutExportOptions {
688
480
  /**
689
481
  * A fitted ship — read a SLEF export, or assemble a hull from scratch.
690
482
  *
483
+ * ## Member index
484
+ *
485
+ * - **Construct** — {@link fromSlef}, {@link fromLoadout}, {@link empty},
486
+ * {@link default}. The constructor is private; every build starts at one of these four.
487
+ * - **Inspect** — {@link shipSymbol}, {@link shipName}, {@link shipIdent},
488
+ * {@link unladenMass}, {@link cargoCapacity}, {@link fuelCapacity}, {@link hullValue},
489
+ * {@link modulesValue}, {@link rebuy}, {@link sourcePurchase}, {@link importOutcomes},
490
+ * {@link slots}, {@link fittedModuleAt}, {@link fittedModules},
491
+ * {@link modulesForSlot}, {@link availableBlueprints},
492
+ * {@link availableExperimentalEffects}, {@link validation | validation()}.
493
+ * - **Edit** — {@link setModule}, {@link removeModule}, {@link repairFixedMount},
494
+ * {@link applyBlueprint}, {@link setExperimentalEffect},
495
+ * {@link completeEngineeringGrade}, {@link setPreEngineeredVariant},
496
+ * {@link clearEngineering}, {@link setModuleEnabled}, {@link setModulePriority}. Each
497
+ * returns `this` unless it reports a result of its own.
498
+ * - **Analyse** — not here. Jump range, mass, cost, power, heat, mobility, shields,
499
+ * armour and firepower live on
500
+ * {@link ships!BuildMetrics | BuildMetrics}, which reads this build:
501
+ * `BuildMetrics.of(build).maxJumpRange()`. The split lets an outfitting editor import
502
+ * the editors without the calculations, and a viewer the calculations without the
503
+ * editors.
504
+ * - **Export** — {@link toLoadoutEvent}, {@link toSlef}, {@link toSlefString}.
505
+ *
506
+ * ## Properties against methods
507
+ *
508
+ * **A property is a fact this build already carries; a method does something.** Every
509
+ * getter above is an identity, an aggregate figure, or what a capture stated — it
510
+ * computes nothing, takes no options, and never throws. Everything that does work is a
511
+ * call: reading the catalogues, enumerating mounts, editing the fit, exporting it, and
512
+ * every figure on {@link ships!BuildMetrics | BuildMetrics}.
513
+ *
514
+ * The rule makes the split predictable, not harmless. Two of those calls take no
515
+ * argument and read like facts — {@link validation | validation()}, which revalidates
516
+ * the whole fit, and {@link fittedModules | fittedModules()}, which allocates and
517
+ * deeply freezes a fresh snapshot on every call — so they are methods by the rule above
518
+ * even though a reader may reach for them as properties. **A forgotten `()` still hands
519
+ * you the function rather than the value**, and in plain JavaScript nothing complains:
520
+ * `build.fittedModules` is a function reference, `build.fittedModules()` is the list.
521
+ * TypeScript catches it; a `.js` consumer will not.
522
+ *
691
523
  * @example
692
524
  * Read a build a player already flies, and ask it what an outfitting screen shows.
693
525
  * Every figure below is one build's — a Krait Phantom explorer. Figures the capture
@@ -695,6 +527,7 @@ interface SlefExportOptions extends LoadoutExportOptions {
695
527
  * describe survives import; the rest are computed from the fit.
696
528
  *
697
529
  * ```ts
530
+ * import { BuildMetrics } from '@elite-dangerous-almanac/core/ships/build-metrics';
698
531
  * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
699
532
  * import type { LoadoutEvent } from '@elite-dangerous-almanac/core/ships/slef';
700
533
  *
@@ -707,10 +540,11 @@ interface SlefExportOptions extends LoadoutExportOptions {
707
540
  * build.shipName; // -> 'Jenny Longuet'
708
541
  * build.unladenMass; // -> 388.830017 (tonnes)
709
542
  *
710
- * build.maxJumpRange(); // -> 60.5478 (ly, best single jump)
711
- * build.powerBudget().withinBudget; // -> true
712
- * build.shieldMetrics()?.strength; // -> 743.12 (MJ)
713
- * build.armourMetrics().hitPoints; // -> 307.8
543
+ * const metrics = BuildMetrics.of(build);
544
+ * metrics.maxJumpRange(); // -> 60.5478 (ly, best single jump)
545
+ * metrics.powerBudget().withinBudget; // -> true
546
+ * metrics.shieldMetrics()?.strength; // -> 743.12 (MJ)
547
+ * metrics.armourMetrics().hitPoints; // -> 307.8
714
548
  * ```
715
549
  */
716
550
  declare class ShipLoadout {
@@ -725,7 +559,8 @@ declare class ShipLoadout {
725
559
  * Defaults to the first.
726
560
  * @returns The loadout for that entry.
727
561
  * @remarks Module normalization follows {@link ShipLoadout.fromLoadout}; inspect
728
- * {@link importOutcomes} for modules that were emptied or defaulted.
562
+ * {@link importOutcomes} for modules that were emptied or defaulted, and for stated
563
+ * engineering it could not resolve.
729
564
  * @throws {SyntaxError} If `input` is a string that is not valid JSON.
730
565
  * @throws {TypeError} If the export holds no usable loadout, `index` is out of range,
731
566
  * or the selected entry names a hull absent from the catalogue.
@@ -744,6 +579,20 @@ declare class ShipLoadout {
744
579
  * and the catalogue's stat block then supplies the values the capture omits; the
745
580
  * capture's own modifiers stay authoritative over it.
746
581
  *
582
+ * **A recipe stated without `Modifiers` is rolled.** A journal writes the modifier
583
+ * block beside the recipe, but SLEF permits stating the recipe alone and Inara does,
584
+ * so such a block is materialised here at the grade and quality it states — otherwise
585
+ * the module would report that it is engineered while publishing the figures of one
586
+ * that is not. Where the module's own engineering menu offers the recipe, the block
587
+ * is read as an ordinary roll of it, even if a fixed article of that module carries
588
+ * the same blueprint at the same grade; use {@link setPreEngineeredVariant} to say
589
+ * the article was meant instead. A Mercenary article is read as its own article at
590
+ * the grade it was bought at, and as that purchase climbed by its bespoke recipe
591
+ * above it. Where the menu does not offer the recipe, no ordinary roll
592
+ * could have written the block, so a single catalogued article answering to it is
593
+ * fitted. Where neither answers, the module keeps unengineered figures and
594
+ * {@link importOutcomes} reports the slot as `unresolvedEngineering`.
595
+ *
747
596
  * Modules are imported as one complete snapshot, so their order does not affect
748
597
  * per-ship count allowances. An entry stands as the event stated it when the mount
749
598
  * can hold the article the catalogue resolves, when its slot is a known cosmetic or
@@ -755,7 +604,7 @@ declare class ShipLoadout {
755
604
  * unresolved symbol, a resolved one the mount refuses, and no entry at all are
756
605
  * corrected alike, each keeping the source's `On`, `Priority` and `Health`. A
757
606
  * removable mount may stand empty, so an article *it* refuses stays where the event
758
- * put it and is reported by {@link validation} instead.
607
+ * put it and is reported by {@link validation | validation()} instead.
759
608
  *
760
609
  * Normalization makes the captured aggregates untrustworthy, so they are dropped:
761
610
  * {@link unladenMass}, {@link cargoCapacity} and {@link fuelCapacity} are recomputed
@@ -818,7 +667,7 @@ declare class ShipLoadout {
818
667
  * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
819
668
  *
820
669
  * const stock = ShipLoadout.default('SideWinder');
821
- * stock.validation.complete; // -> true
670
+ * stock.validation().complete; // -> true
822
671
  * stock.fittedModuleAt('FrameShiftDrive')?.symbol;
823
672
  * // -> 'Int_Hyperdrive_Size2_Class1'
824
673
  * ```
@@ -864,14 +713,14 @@ declare class ShipLoadout {
864
713
  * unknown — including after an edit or import normalization discarded an import's
865
714
  * figure, since no catalogue records what a replaced module was bought for. Unlike
866
715
  * mass and capacity it is not recomputed from what remains; {@link sourcePurchase}
867
- * keeps the captured figure and {@link buildCost} prices the current fit.
716
+ * keeps the captured figure and {@link ships!BuildMetrics.buildCost | BuildMetrics.buildCost} prices the current fit.
868
717
  */
869
718
  get modulesValue(): number | null;
870
719
  /**
871
720
  * Insurance rebuy cost in credits represented by the build, or `null` if
872
721
  * unknown. Discarded by an edit or by import normalization for the same reason as
873
722
  * {@link modulesValue}, and likewise kept by {@link sourcePurchase};
874
- * {@link buildCost} rebuys the current fit at catalogue prices instead.
723
+ * {@link ships!BuildMetrics.buildCost | BuildMetrics.buildCost} rebuys the current fit at catalogue prices instead.
875
724
  */
876
725
  get rebuy(): number | null;
877
726
  /**
@@ -909,18 +758,23 @@ declare class ShipLoadout {
909
758
  */
910
759
  get sourcePurchase(): SourcePurchaseRecord | null;
911
760
  /**
912
- * Changes made while importing this build, in source order, followed by the fixed
913
- * mounts stocked from the hull defaults because the source named none, in the
914
- * defaults' own order.
761
+ * What the import made of this build: the changes it applied, in source order,
762
+ * followed by the fixed mounts stocked from the hull defaults because the source
763
+ * named none, in the defaults' own order, followed by the modules whose stated
764
+ * engineering it could not resolve.
915
765
  *
916
766
  * @returns A deeply frozen list. It is empty for builds created with
917
767
  * {@link ShipLoadout.empty} or {@link ShipLoadout.default}, and for imports that
918
- * needed no normalization.
768
+ * needed no normalization and left no engineering unresolved.
919
769
  * @remarks
920
770
  * Each entry names the exact slot, and the source identity where the source gave one.
921
771
  * `emptied` means an unknown module was removed from a removable mount; `defaulted`
922
772
  * names the stock article fitted to armour, a core internal or the cargo hatch, with
923
773
  * a `null` `sourceSymbol` when the source named nothing there at all.
774
+ * `unresolvedEngineering` is the one entry that reports a *non*-change: the source
775
+ * stated a recipe and no `Modifiers`, nothing the catalogues carry answers to it, and
776
+ * that module alone keeps the figures of an unengineered one (see
777
+ * {@link ShipLoadout.fromLoadout}).
924
778
  */
925
779
  get importOutcomes(): readonly LoadoutImportOutcome[];
926
780
  /**
@@ -928,13 +782,33 @@ declare class ShipLoadout {
928
782
  *
929
783
  * @remarks
930
784
  * `valid` asks whether the fit is legal: a module in a nonexistent or incompatible
931
- * slot, a duplicated exclusive family, or a module count past the build's allowance
932
- * makes it `false`. `complete` asks that *and* whether armour and the seven core
933
- * mounts are filled — every build fills those, so on a build the two answers agree.
934
- * Neither question reports import normalization, so read {@link importOutcomes}
935
- * beside them.
936
- */
937
- get validation(): LoadoutValidation;
785
+ * slot, a duplicated exclusive family, a module count past the build's allowance, or
786
+ * a ship heavier than its own thrusters can move makes it `false`. `complete` asks
787
+ * that *and* whether armour and the seven core mounts are filled — every build fills
788
+ * those, so on a build the two answers agree. Neither question reports import
789
+ * normalization, so read {@link importOutcomes} beside them.
790
+ *
791
+ * The thruster rule weighs the fitted thrusters' post-engineering `maxMass` against
792
+ * what the ship comes to at each load it can reach without being re-fitted:
793
+ * {@link unladenMass} alone, then with a full {@link fuelCapacity | main tank}, then
794
+ * with a full {@link cargoCapacity | hold} as well. The lightest of those that is
795
+ * already too heavy is what gets reported. A ship that cannot move on a full tank is
796
+ * an error — it never leaves the pad, where the tank always is one — while a ship
797
+ * that only fails with the hold full is a warning, and leaves the build valid and
798
+ * complete: how much cargo to take is the pilot's call. Either way
799
+ * {@link ships!BuildMetrics.mobilityMetrics | BuildMetrics.mobilityMetrics} reports
800
+ * a speed of zero at the load in question.
801
+ *
802
+ * A capture may state a mass nobody can weigh — a negative `UnladenMass`, or an
803
+ * engineering modifier that drives a rating below zero. Neither is refused here:
804
+ * this method reports a build rather than rejecting one, so an unweighable figure is
805
+ * left out and the rule it feeds simply does not run. The figure itself is still
806
+ * reported, as a thrown one, by whichever {@link ships!BuildMetrics | BuildMetrics}
807
+ * calculation reads it.
808
+ *
809
+ * @returns The validation report, recomputed from the current fit on every call.
810
+ */
811
+ validation(): LoadoutValidation;
938
812
  /**
939
813
  * Frozen point-in-time views of the hull's mounts in outfitting-panel order.
940
814
  *
@@ -991,7 +865,7 @@ declare class ShipLoadout {
991
865
  *
992
866
  * declare const build: ShipLoadout;
993
867
  *
994
- * build.availableBlueprints('FrameShiftDrive').map(({ fdname }) => fdname);
868
+ * build.availableBlueprints('FrameShiftDrive').map(({ blueprintSymbol }) => blueprintSymbol);
995
869
  * ```
996
870
  */
997
871
  availableBlueprints(slotKey: string): readonly AvailableBlueprint[];
@@ -1018,6 +892,13 @@ declare class ShipLoadout {
1018
892
  * satisfied, with candidates that would worsen a one-per-ship or module-count limit
1019
893
  * omitted.
1020
894
  *
895
+ * @remarks
896
+ * This is the outfitting *offer*, so the fifteen {@link ships!OutfittingModule.grantOnly | grantOnly}
897
+ * articles are never in it: each is a second identity for a module the game already
898
+ * sells — `Int_Engine_Size2_Class1_free` is the 2E Thrusters — and listing both puts
899
+ * the same article on the screen twice, once with no price. A build that already
900
+ * carries one keeps it; only the choices are filtered.
901
+ *
1021
902
  * @param slotKey - The slot key to fit, matched case-insensitively (journal spelling).
1022
903
  * @returns The fitting modules, in complete-catalogue order.
1023
904
  * @throws {RangeError} If the hull has no slot with that key.
@@ -1131,17 +1012,17 @@ declare class ShipLoadout {
1131
1012
  *
1132
1013
  * @param slotKey - The slot whose module to engineer, matched case-insensitively
1133
1014
  * (journal spelling).
1134
- * @param fdname - The blueprint recipe's Frontier `fdname`, e.g. `"FSD_LongRange"`.
1015
+ * @param blueprintSymbol - The blueprint recipe's Frontier symbol, e.g. `"FSD_LongRange"`.
1135
1016
  * @param options - {@link ApplyBlueprintOptions}: `grade` (1–5), optional `quality`
1136
- * (0–1, default 1), and optional `experimental` effect `fdname`. A nullish
1017
+ * (0–1, default 1), and optional `experimental` effect symbol. A nullish
1137
1018
  * `experimental` means no effect, the same as leaving it out. Each is read once,
1138
1019
  * before anything is checked, so an accessor cannot answer the check and the use
1139
1020
  * differently.
1140
1021
  * @returns `this`, for chaining.
1141
1022
  * @throws {RangeError} If the slot is empty, or the blueprint/grade/experimental is
1142
1023
  * unknown, or `quality` is outside `[0, 1]`.
1143
- * @throws {TypeError} If `slotKey` or `fdname` is not a string, `options` is not an
1144
- * object, or `options.experimental` carries a value that is not a string — a nullish
1024
+ * @throws {TypeError} If `slotKey` or `blueprintSymbol` is not a string, `options` is not an
1025
+ * object, or `options.experimentalEffectSymbol` carries a value that is not a string — a nullish
1145
1026
  * one is no effect, not a wrong type. Also if the fitted module has no stats to
1146
1027
  * engineer, is final and accepts no further engineering, is not offered the blueprint
1147
1028
  * by its own menu, is not offered the experimental effect by it, or the id names a
@@ -1151,6 +1032,7 @@ declare class ShipLoadout {
1151
1032
  * stored as a partial journal modifier block.
1152
1033
  * @example
1153
1034
  * ```ts
1035
+ * import { BuildMetrics } from '@elite-dangerous-almanac/core/ships/build-metrics';
1154
1036
  * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
1155
1037
  * import { CORE_MODULES } from '@elite-dangerous-almanac/core/ships/modules-core';
1156
1038
  * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
@@ -1162,12 +1044,12 @@ declare class ShipLoadout {
1162
1044
  * build.setModule('FrameShiftDrive', fsd)
1163
1045
  * .applyBlueprint('FrameShiftDrive', 'FSD_LongRange', {
1164
1046
  * grade: 5,
1165
- * experimental: 'special_fsd_heavy',
1047
+ * experimentalEffectSymbol: 'special_fsd_heavy',
1166
1048
  * });
1167
- * build.maxJumpRange(); // uses the engineered optimal mass
1049
+ * BuildMetrics.of(build).maxJumpRange(); // uses the engineered optimal mass
1168
1050
  * ```
1169
1051
  */
1170
- applyBlueprint(slotKey: string, fdname: string, options: ApplyBlueprintOptions): this;
1052
+ applyBlueprint(slotKey: string, blueprintSymbol: string, options: ApplyBlueprintOptions): this;
1171
1053
  /**
1172
1054
  * Add, replace or remove only the fitted module's experimental effect.
1173
1055
  *
@@ -1177,7 +1059,7 @@ declare class ShipLoadout {
1177
1059
  * leave the build unchanged and return stable structured data.
1178
1060
  *
1179
1061
  * @param slotKey - The engineered slot, matched case-insensitively.
1180
- * @param experimental - Experimental-effect `fdname`, or `null` to remove the effect.
1062
+ * @param experimental - Experimental-effect symbol, or `null` to remove the effect.
1181
1063
  * @returns A frozen result identifying an update, no-op or lossless refusal.
1182
1064
  * @throws {TypeError} If `slotKey` or a non-null `experimental` is not a string.
1183
1065
  * @example
@@ -1200,9 +1082,13 @@ declare class ShipLoadout {
1200
1082
  * purchase identity. A refusal never changes the loadout.
1201
1083
  *
1202
1084
  * A block that names a blueprint and grade but states no `Modifiers` at all is rolled
1203
- * here too, even at quality `1` — SLEF permits that identity-only shape and Inara
1204
- * writes it, so a capture of a completed roll would otherwise stay stock. A stated
1205
- * modifier array, empty or partial, is left alone at quality `1`.
1085
+ * here too, even at quality `1`, so a completed roll cannot stay stock. On an imported
1086
+ * build such a block is one {@link ShipLoadout.fromLoadout} did not roll: a catalogued
1087
+ * article it fitted instead, whose fixed modifiers this spells out unless the article
1088
+ * is final; a Mercenary article at the grade it was bought at, whose block no registry
1089
+ * publishes and which this refuses; or a recipe nothing answered to, which this
1090
+ * refuses for the reason the import could not roll it. A stated modifier array, empty
1091
+ * or partial, is left alone at quality `1`.
1206
1092
  *
1207
1093
  * @param slotKey - The engineered slot, matched case-insensitively.
1208
1094
  * @returns A frozen result identifying a normalized, unchanged or unsupported state.
@@ -1247,7 +1133,7 @@ declare class ShipLoadout {
1247
1133
  * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1248
1134
  *
1249
1135
  * const festive = getPreEngineeredVariants('Hpt_FlakMortar_Turret_Medium')
1250
- * .find((variant) => variant.blueprint === 'Decorative_Red')!;
1136
+ * .find((variant) => variant.blueprintSymbol === 'Decorative_Red')!;
1251
1137
  * const build = ShipLoadout.empty('Krait_MkII')
1252
1138
  * .setPreEngineeredVariant('MediumHardpoint1', festive);
1253
1139
  * build.fittedModuleAt('MediumHardpoint1')?.effectiveStats?.damage; // -> 0.34
@@ -1292,7 +1178,7 @@ declare class ShipLoadout {
1292
1178
  *
1293
1179
  * @param slotKey - The slot's journal key, matched case-insensitively.
1294
1180
  * @param priority - The journal's **zero-based** group, `0`–`4`. Note that the
1295
- * outfitting panel — and {@link powerBudget}'s `bands[].priority` — number the same
1181
+ * outfitting panel — and {@link ships!BuildMetrics.powerBudget | BuildMetrics.powerBudget}'s `bands[].priority` — number the same
1296
1182
  * five groups `1`–`5`.
1297
1183
  * @returns `this`, for chaining.
1298
1184
  * @throws {RangeError} If the slot is empty, or `priority` is not an integer in `[0, 4]`.
@@ -1353,531 +1239,6 @@ declare class ShipLoadout {
1353
1239
  * ```
1354
1240
  */
1355
1241
  toSlefString(options: SlefExportOptions): string;
1356
- /**
1357
- * The resolved frame-shift-drive constants for this build — post-engineering,
1358
- * with any Guardian FSD Booster folded into `jumpBoost`.
1359
- *
1360
- * @throws {TypeError} If the fitted drive's record is missing any of its required
1361
- * jump constants.
1362
- */
1363
- get frameShiftDrive(): FrameShiftDriveParams;
1364
- /**
1365
- * The fitted thrusters' post-engineering mass curve, or `null` when the build has
1366
- * none — the thruster counterpart of {@link frameShiftDrive}.
1367
- *
1368
- * @remarks
1369
- * A {@link ships!ThrusterParams | ThrusterParams} carries the three masses the
1370
- * curve is defined over and the multiplier at each, plus the separate `speedCurve`
1371
- * and `rotationCurve` an enhanced-performance thruster refines them with. Pass it
1372
- * straight to
1373
- * {@link ships!thrusterMassCurveMultiplier | thrusterMassCurveMultiplier} for the
1374
- * multiplier at a mass of your own, or read `optMass` and `maxMass` against
1375
- * {@link ships!MobilityMetrics.loadedMass | loadedMass} for where this build sits
1376
- * on the curve.
1377
- *
1378
- * This is the fitted article's curve, so a switched-off or shed thruster still has
1379
- * one; {@link mobilityMetricsResult} is what judges whether the build can use it.
1380
- * It answers `null` rather than throwing — unlike `frameShiftDrive`, which the jump
1381
- * equation cannot do without — when no thrusters are fitted or the fitted record
1382
- * carries no complete curve.
1383
- *
1384
- * @example
1385
- * ```ts
1386
- * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1387
- *
1388
- * const build = ShipLoadout.default('Anaconda');
1389
- * build.thrusters?.optMass; // -> 1440, tonnes
1390
- * build.thrusters?.maxMass; // -> 2160, past which the ship does not move at all
1391
- * ```
1392
- */
1393
- get thrusters(): ThrusterParams | null;
1394
- /**
1395
- * The fitted frame shift drive's dimensionless mass factor at a chosen load.
1396
- *
1397
- * @param options - {@link JumpOptions}. `fuel` defaults to a full main tank and
1398
- * `cargo` to `0`.
1399
- * @returns `optMass / loadedMass`: `1` at the drive's optimised mass, below `1`
1400
- * above it and above `1` below it.
1401
- * @remarks
1402
- * This is the mass term used by the jump equation, not the three-point performance
1403
- * curve used by thrusters and shield generators. Main-tank fuel contributes to the
1404
- * loaded mass; the Guardian FSD Booster's additive range does not contribute to the
1405
- * factor.
1406
- * @throws {TypeError} If the build has no usable frame shift drive.
1407
- * @throws {RangeError} If fuel or cargo is not finite and non-negative, or loaded
1408
- * mass is zero.
1409
- * @example
1410
- * ```ts
1411
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1412
- *
1413
- * declare const build: ShipLoadout;
1414
- * build.frameShiftDriveMassFactor({ fuel: 8, cargo: 32 }); // dimensionless
1415
- * ```
1416
- */
1417
- frameShiftDriveMassFactor(options?: JumpOptions): number;
1418
- /**
1419
- * Best single-jump range, in light-years — no cargo, and exactly one jump's fuel
1420
- * aboard (the lightest the ship jumps). This is the figure the game and EDSY label
1421
- * "maximum jump range".
1422
- *
1423
- * @returns The best single jump, in light-years, or `0` for a capture that states a
1424
- * main tank of `0`.
1425
- * @throws {TypeError} If the build has no usable frame shift drive.
1426
- */
1427
- maxJumpRange(): number;
1428
- /**
1429
- * The range of a single jump for a chosen fuel and cargo load, in light-years.
1430
- *
1431
- * @param options - {@link JumpOptions}. `fuel` defaults to a full main tank,
1432
- * `cargo` to `0`.
1433
- * @returns The jump's range, in light-years.
1434
- * @throws {TypeError} If the build has no usable frame shift drive.
1435
- * @throws {RangeError} If fuel or cargo is not finite and non-negative.
1436
- */
1437
- jumpRange(options?: JumpOptions): number;
1438
- /**
1439
- * Single-jump range on a full tank with a full cargo hold, in light-years.
1440
- *
1441
- * @returns The jump's range, in light-years.
1442
- * @throws {TypeError} If the build has no usable frame shift drive.
1443
- */
1444
- ladenJumpRange(): number;
1445
- /**
1446
- * The fuel a single jump of a given distance costs, in tonnes.
1447
- *
1448
- * @param distance - The jump distance, in light-years.
1449
- * @param options - {@link JumpOptions}. `fuel` defaults to a full main tank,
1450
- * `cargo` to `0`.
1451
- * @returns Fuel used, in tonnes (capped at the drive's max fuel per jump).
1452
- * @throws {TypeError} If the build has no usable frame shift drive.
1453
- * @throws {RangeError} If fuel or cargo is not finite and non-negative.
1454
- */
1455
- fuelPerJump(distance: number, options?: JumpOptions): number;
1456
- /**
1457
- * Total range and jump count for a chosen fuel and cargo load.
1458
- *
1459
- * @param options - {@link JumpOptions}. `fuel` defaults to a full main tank,
1460
- * `cargo` to `0`.
1461
- * @returns Summed range in light-years and the jumps made before the tank is empty.
1462
- * @throws {TypeError} If the build has no usable frame shift drive.
1463
- * @throws {RangeError} If fuel or cargo is not finite and non-negative, or the
1464
- * fuel load would require more than 100,000 jumps.
1465
- * @example
1466
- * ```ts
1467
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1468
- *
1469
- * declare const build: ShipLoadout;
1470
- * build.totalRange().jumps; // jumps available from one full main tank
1471
- * build.totalRange({ fuel: 8, cargo: 32 }).range; // range for that partial load
1472
- * ```
1473
- */
1474
- totalRange(options?: JumpOptions): TotalRangeDetails;
1475
- /**
1476
- * Resolve one of the package's standard load conditions for jump and mobility views.
1477
- *
1478
- * @param load - `'maximum'` for one jump's fuel and no cargo, `'unladen'` for a
1479
- * full main tank and no cargo, or `'laden'` for a full main tank and full hold.
1480
- * @returns The fuel and cargo carried, and the {@link StandardLoadInputs.mass} the
1481
- * ship weighs carrying them, all in tonnes. Only `'maximum'` can come back
1482
- * incomplete: it validates the whole fitted drive, jump booster included, so a
1483
- * complete one can be passed straight to {@link jumpRange}.
1484
- * @throws {RangeError} If `load` is not a recognised standard load.
1485
- * @example
1486
- * ```ts
1487
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1488
- *
1489
- * declare const build: ShipLoadout;
1490
- * const load = build.standardLoadResult('maximum');
1491
- * if (load.complete) build.mobilityMetrics({ ...load.value, enginesPips: 2 });
1492
- * ```
1493
- * @example
1494
- * ```ts
1495
- * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1496
- *
1497
- * const build = ShipLoadout.default('Anaconda');
1498
- * build.standardLoadResult('laden').value?.mass; // -> 1210, tonnes with a full tank and hold
1499
- * ```
1500
- */
1501
- standardLoadResult(load: StandardLoad): CalculationResult<StandardLoadInputs>;
1502
- /**
1503
- * Every jump figure at once — best, unladen, laden, and each load's total.
1504
- *
1505
- * @returns The {@link JumpRangeSummary}. Single-jump figures and each total's
1506
- * `range` are in light-years. For a partial load, call {@link jumpRange} for one
1507
- * jump or {@link totalRange} for every jump with the `fuel` and `cargo` you
1508
- * actually have.
1509
- * @throws {TypeError} If the build has no usable frame shift drive.
1510
- * @example
1511
- * ```ts
1512
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1513
- *
1514
- * declare const build: ShipLoadout;
1515
- *
1516
- * const jumps = build.jumpRangeSummary();
1517
- * jumps.max; // -> 89.41 (one jump's fuel, empty hold)
1518
- * jumps.laden; // -> the range with the hold full
1519
- * jumps.totalMax.jumps; // the best jump expressed as a total
1520
- * // Half a tank and 32 t aboard:
1521
- * build.jumpRange({ fuel: build.fuelCapacity.main / 2, cargo: 32 });
1522
- * ```
1523
- */
1524
- jumpRangeSummary(): JumpRangeSummary;
1525
- /**
1526
- * The build's power budget: what the plant makes, what the modules draw with
1527
- * hardpoints retracted and deployed, and which priority groups stay lit.
1528
- *
1529
- * Draws are post-engineering, modules switched off in the journal are skipped, and
1530
- * weapons (plus the utility fittings that are not always powered) count only
1531
- * towards the deployed total.
1532
- *
1533
- * @returns The {@link PowerBudget}. `consumers` includes modules with positive
1534
- * draw; passive and zero-draw fittings are absent.
1535
- * @throws {RangeError} If a power capacity or module draw is negative or not finite.
1536
- * @example
1537
- * ```ts
1538
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1539
- *
1540
- * declare const build: ShipLoadout;
1541
- *
1542
- * const power = build.powerBudget();
1543
- * power.available; // -> 20.4 MW generated
1544
- * power.deployed; // -> 19.02 MW drawn, hardpoints out
1545
- * power.withinBudget; // -> true
1546
- * power.bands[4]?.poweredDeployed; // -> is priority group 5 still lit?
1547
- * ```
1548
- */
1549
- powerBudget(): PowerBudget;
1550
- /**
1551
- * The build's heat: what it idles at, what it runs at flying and jumping, and
1552
- * whether firing everything cooks it.
1553
- *
1554
- * Every figure is post-engineering. The heat a build makes follows what the plant
1555
- * actually feeds, so a module switched off — or one in a priority group the plant
1556
- * cannot keep lit — contributes nothing.
1557
- *
1558
- * @returns The {@link HeatMetrics}, or `null` when the build has no powered power
1559
- * plant.
1560
- * @example
1561
- * ```ts
1562
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1563
- *
1564
- * declare const build: ShipLoadout;
1565
- *
1566
- * const heat = build.heatMetrics();
1567
- * heat?.idle.gauge; // -> 0.23, i.e. the gauge reads 23%
1568
- * heat?.firingSustained.overheats; // -> false: the guns run cool enough to hold
1569
- * heat?.firingDrained.secondsToOverheat; // -> how long an alpha strike has on an empty WEP
1570
- * ```
1571
- */
1572
- heatMetrics(): HeatMetrics | null;
1573
- /**
1574
- * The build's speed, boost and rotation rates at a chosen load and ENG allocation.
1575
- *
1576
- * @remarks
1577
- * Main-tank fuel contributes to the flight model's loaded mass. Reserve-tank fuel
1578
- * does not: although the statistics panel includes it in the displayed current
1579
- * mass, ten observed builds reproduce their angular rates only when the reserve is
1580
- * excluded from the thruster mass curve.
1581
- *
1582
- * @param options - Fuel defaults to a full main tank, cargo to `0`, and ENG pips to `4`.
1583
- * @returns Loaded {@link MobilityMetrics}, or `null` when no fully described
1584
- * thrusters are powered with hardpoints retracted. Use
1585
- * {@link mobilityMetricsResult} to distinguish the unavailable conditions.
1586
- * @throws {RangeError} If fuel or cargo is not finite and non-negative, or
1587
- * `enginesPips` is outside `[0, 4]`.
1588
- * @example
1589
- * ```ts
1590
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1591
- *
1592
- * declare const build: ShipLoadout;
1593
- * build.mobilityMetrics({ cargo: 32, fuel: 8, enginesPips: 2 })?.speed; // -> m/s
1594
- * ```
1595
- */
1596
- mobilityMetrics(options?: MobilityOptions): MobilityMetrics | null;
1597
- /**
1598
- * The build's mobility with a diagnostic when its thrusters or retracted power
1599
- * supply is unavailable.
1600
- *
1601
- * @param options - Fuel defaults to a full main tank, cargo to `0`, and ENG pips to `4`.
1602
- * @returns A complete {@link MobilityMetrics} value, otherwise `null` plus the input
1603
- * or fitted-module state that prevented the calculation.
1604
- * @throws {RangeError} If fuel or cargo is not finite and non-negative, or
1605
- * `enginesPips` is outside `[0, 4]`.
1606
- * @example
1607
- * ```ts
1608
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1609
- *
1610
- * declare const build: ShipLoadout;
1611
- * const result = build.mobilityMetricsResult({ enginesPips: 2 });
1612
- * if (result.complete) result.value.speed; // metres per second
1613
- * else result.issues[0].reason; // unavailable-state discriminator
1614
- * ```
1615
- */
1616
- mobilityMetricsResult(options?: MobilityOptions): CalculationResult<MobilityMetrics>;
1617
- /**
1618
- * The build's shields: strength in megajoules, where it comes from, and the
1619
- * effective resistances.
1620
- *
1621
- * Shield strength scales with the **hull's** mass, not the build's, so fitting
1622
- * more modules never weakens it. Boosters, Guardian shield reinforcement and any
1623
- * engineering are all folded in; switched-off or shed boosters and reinforcement
1624
- * are ignored, while a switched-off or shed generator makes the metric unavailable.
1625
- *
1626
- * @param options - {@link DefenceOptions}. `systemsPips` (0–4) folds the SYS
1627
- * capacitor's own resistance into the reported figures; it defaults to `0`, which
1628
- * is what an outfitting screen shows.
1629
- * @returns The {@link ShieldMetrics}, or `null` when the build has no shield
1630
- * generator powered with hardpoints retracted. Use
1631
- * {@link shieldMetricsResult} to distinguish the unavailable conditions.
1632
- * @throws {RangeError} If `systemsPips` is outside `[0, 4]` or not finite.
1633
- * @example
1634
- * ```ts
1635
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1636
- *
1637
- * declare const build: ShipLoadout;
1638
- *
1639
- * const shields = build.shieldMetrics();
1640
- * shields?.strength; // -> MJ
1641
- * shields?.resistances.thermal; // -> negative on a stock generator
1642
- * build.shieldMetrics({ systemsPips: 4 })?.resistances.thermal; // -> with 4 pips to SYS
1643
- * ```
1644
- */
1645
- shieldMetrics(options?: DefenceOptions): ShieldMetrics | null;
1646
- /**
1647
- * The build's shields with a diagnostic when its hull, generator or retracted
1648
- * power supply is unavailable.
1649
- *
1650
- * @param options - {@link DefenceOptions}. `systemsPips` defaults to `0`.
1651
- * @returns A complete {@link ShieldMetrics} value, otherwise `null` plus the input
1652
- * or fitted-module state that prevented the calculation.
1653
- * @throws {RangeError} If `systemsPips` is outside `[0, 4]` or not finite.
1654
- * @example
1655
- * ```ts
1656
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1657
- *
1658
- * declare const build: ShipLoadout;
1659
- * const result = build.shieldMetricsResult();
1660
- * if (result.complete) result.value.strength; // megajoules
1661
- * else result.issues[0].reason; // unavailable-state discriminator
1662
- * ```
1663
- */
1664
- shieldMetricsResult(options?: DefenceOptions): CalculationResult<ShieldMetrics>;
1665
- /**
1666
- * Time for this build's shield to rise after collapse and then regenerate to full.
1667
- *
1668
- * @param options - SYS pips in `[0, 4]`, defaulting to `4`.
1669
- * @returns Recovery rates and seconds, or `null` when no shield generator is powered
1670
- * with hardpoints retracted. Use
1671
- * {@link shieldRecoveryResult} to distinguish the unavailable conditions.
1672
- * Insufficient zero-pip recharge produces `Infinity`.
1673
- * @throws {RangeError} If `systemsPips` is outside `[0, 4]` or not finite.
1674
- * @example
1675
- * ```ts
1676
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1677
- *
1678
- * declare const build: ShipLoadout;
1679
- * build.shieldRecovery({ systemsPips: 4 })?.recoveryTime; // -> seconds from collapse to 50%
1680
- * ```
1681
- */
1682
- shieldRecovery(options?: DefenceOptions): ShieldRecovery | null;
1683
- /**
1684
- * The build's shield recovery with a diagnostic when its hull, generator or
1685
- * retracted power supply is unavailable.
1686
- *
1687
- * @param options - SYS pips in `[0, 4]`, defaulting to `4`.
1688
- * @returns A complete {@link ShieldRecovery} value, otherwise `null` plus the input
1689
- * or fitted-module state that prevented the calculation.
1690
- * @throws {RangeError} If `systemsPips` is outside `[0, 4]` or not finite.
1691
- * @example
1692
- * ```ts
1693
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1694
- *
1695
- * declare const build: ShipLoadout;
1696
- * const result = build.shieldRecoveryResult();
1697
- * if (result.complete) result.value.recoveryTime; // seconds
1698
- * else result.issues[0].reason; // unavailable-state discriminator
1699
- * ```
1700
- */
1701
- shieldRecoveryResult(options?: DefenceOptions): CalculationResult<ShieldRecovery>;
1702
- /**
1703
- * Every fitted shield cell bank and the usable rearmed reinforcement pool.
1704
- *
1705
- * Every fitted bank remains in `banks`, where `powered` says whether it is switched
1706
- * on and its priority group is fed with hardpoints deployed. The totals include only
1707
- * those powered banks, so a build whose plant is switched off or outdrawn reports
1708
- * every bank unpowered and zero totals.
1709
- *
1710
- * @returns A frozen {@link CellBankSummary}; no banks is an empty list and zero totals.
1711
- * @example
1712
- * ```ts
1713
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1714
- *
1715
- * declare const build: ShipLoadout;
1716
- * build.cellBanks().totalRestorable; // -> MJ across every powered fitted cell
1717
- * ```
1718
- */
1719
- cellBanks(): CellBankSummary;
1720
- /**
1721
- * Price the whole build from the catalogues: shop credits, Merc Coin and the
1722
- * engineering materials its modifications consume.
1723
- *
1724
- * No modification is charged twice. A Mercenary article arrives at the grade it was sold at,
1725
- * so only the climb above that grade bills materials and further Merc Coin, and an
1726
- * experimental effect the article came with is free while one added on top is not. A
1727
- * fixed reward article — festive, Guardian, community-goal — identifies a recipe it
1728
- * was never rolled from, so it contributes no materials at all.
1729
- *
1730
- * @returns A frozen {@link BuildCost}. `credits.modules`, `credits.total` and
1731
- * `credits.rebuy` are lower bounds while {@link BuildCredits.unpriced} is non-empty;
1732
- * built-in hull fittings are free rather than unpriced.
1733
- * @remarks
1734
- * This is the one place `ShipLoadout` reads the material and Merc Coin cost
1735
- * catalogues, which is why the facade carries them; import
1736
- * {@link ships/blueprint-costs!getBlueprintCost | getBlueprintCost} and
1737
- * {@link ships/experimental-effect-costs!getExperimentalEffectCost | getExperimentalEffectCost}
1738
- * directly to price one recipe without a build.
1739
- * @example
1740
- * ```ts
1741
- * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1742
- *
1743
- * const build = ShipLoadout.default('Anaconda');
1744
- * build.buildCost().credits.hull; // -> 142456440
1745
- * build.applyBlueprint('FrameShiftDrive', 'FSD_LongRange', { grade: 5 });
1746
- * build.buildCost().materials.find((material) => material.symbol === 'Arsenic')?.count; // -> 5
1747
- * ```
1748
- * @example
1749
- * ```ts
1750
- * import { getPreEngineeredVariants } from '@elite-dangerous-almanac/core/ships/pre-engineered';
1751
- * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1752
- *
1753
- * const variant = getPreEngineeredVariants('Hpt_Railgun_Fixed_Medium')
1754
- * .find((candidate) => candidate.acquisition === 'mercenary')!;
1755
- * const build = ShipLoadout.default('Python')
1756
- * .setPreEngineeredVariant('MediumHardpoint1', variant);
1757
- * build.buildCost().mercCoins; // -> 950
1758
- * ```
1759
- */
1760
- buildCost(): BuildCost;
1761
- /**
1762
- * Weigh the whole build: the hull, the fitted modules, and the load on top of them.
1763
- *
1764
- * @remarks
1765
- * The mass companion to {@link buildCost}, answering the same question in tonnes
1766
- * that that one answers in credits. Every module's mass is post-engineering, so a
1767
- * Lightweight roll is already in `modules`.
1768
- *
1769
- * The reserve tank is **not** counted. The main tank is the fuel the drive and the
1770
- * flight model see, and it is what {@link jumpRange} and {@link mobilityMetrics}
1771
- * weigh; the game's statistics panel additionally counts the reserve in the current
1772
- * mass it displays, so add {@link fuelCapacity}`.reserve` to reproduce that reading.
1773
- *
1774
- * @param options - {@link JumpOptions}. `fuel` defaults to a full main tank and
1775
- * `cargo` to `0`, matching {@link jumpRange} and {@link mobilityMetrics}. Pass
1776
- * {@link standardLoadResult} to weigh one of the standard loads.
1777
- * @returns A frozen {@link BuildMass}, every figure in tonnes.
1778
- * @throws {RangeError} If fuel or cargo is not finite and non-negative.
1779
- * @example
1780
- * ```ts
1781
- * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1782
- *
1783
- * const build = ShipLoadout.default('Anaconda');
1784
- * const mass = build.buildMass();
1785
- * mass.hull; // -> 400
1786
- * mass.modules; // -> 664
1787
- * mass.total; // -> 1096, a full main tank and an empty hold
1788
- * build.buildMass({ cargo: build.cargoCapacity }).total; // -> 1210
1789
- * ```
1790
- */
1791
- buildMass(options?: JumpOptions): BuildMass;
1792
- /**
1793
- * The build's armour: hull hit points, the bulkhead and reinforcement each
1794
- * contribute, and the effective resistances.
1795
- *
1796
- * @returns The {@link ArmourMetrics}, read off the fitted bulkhead.
1797
- * @example
1798
- * ```ts
1799
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1800
- *
1801
- * declare const build: ShipLoadout;
1802
- *
1803
- * const hull = build.armourMetrics();
1804
- * hull.hitPoints; // -> total hull points
1805
- * hull.resistances.explosive; // -> lightweight alloy is explosively weak
1806
- * hull.effectiveHitPoints.thermal; // -> thermal damage the hull can soak
1807
- * ```
1808
- */
1809
- armourMetrics(): ArmourMetrics;
1810
- /**
1811
- * The build's firepower: DPS, sustained DPS, weapons-capacitor draw, heat and power
1812
- * draw for every fitted weapon, plus the totals.
1813
- *
1814
- * Every figure is post-engineering. A weapon switched off in the journal is still
1815
- * listed — with its own metrics — but left out of the totals.
1816
- *
1817
- * @returns The {@link BuildWeaponMetrics}.
1818
- * @example
1819
- * ```ts
1820
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1821
- *
1822
- * declare const build: ShipLoadout;
1823
- *
1824
- * const guns = build.weaponMetrics();
1825
- * guns.total.damagePerSecond; // -> burst DPS across the hardpoints
1826
- * guns.total.sustainedDamagePerSecond; // -> with reloads folded in
1827
- * guns.total.energyPerSecond; // -> MW asked of the WEP capacitor
1828
- * guns.total.powerDraw; // -> MW asked of the power plant when deployed
1829
- * guns.weapons[0]?.metrics.damageByType.thermal;
1830
- * guns.weapons[0]?.maximumRange; // post-engineering metres, when known
1831
- * guns.weapons[0]?.armourPiercing; // post-engineering rating, when known
1832
- * guns.weapons[0]?.ammunition?.total; // -> rounds aboard when fully rearmed
1833
- * ```
1834
- */
1835
- weaponMetrics(): BuildWeaponMetrics;
1836
- /**
1837
- * WEP-capacitor recharge and endurance while every powered weapon fires.
1838
- *
1839
- * @param options - WEP pips in `[0, 4]`, defaulting to `4`.
1840
- * @returns Actual recharge, sustained draw, net drain and seconds from full to
1841
- * empty. The deployed power budget is applied to the distributor and weapons, so a
1842
- * module the plant sheds contributes nothing. With no powered distributor, capacity
1843
- * and recharge are zero. A load that draws no more than recharge reports
1844
- * `Infinity` for `timeToDrain`.
1845
- * @throws {RangeError} If `weaponsPips` is outside `[0, 4]` or not finite.
1846
- * @example
1847
- * ```ts
1848
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1849
- *
1850
- * declare const build: ShipLoadout;
1851
- * build.weaponsCapacitorMetrics({ weaponsPips: 2 }).timeToDrain; // seconds
1852
- * ```
1853
- */
1854
- weaponsCapacitorMetrics(options?: WeaponsOptions): WeaponsCapacitorMetrics;
1855
- /**
1856
- * All three power-distributor capacitors at selected pip allocations.
1857
- *
1858
- * @param options - SYS, ENG and WEP pips in `[0, 4]`, each defaulting
1859
- * independently to `4`. The allocations need not sum to six, which permits
1860
- * independent comparisons of the three maxima.
1861
- * @returns Capacity, rated four-pip recharge and actual pip-scaled recharge for
1862
- * SYS, ENG and WEP, or `null` when the distributor is switched off, its six
1863
- * capacitor stats cannot be resolved, or the retracted power budget sheds it. That
1864
- * retracted state represents the distributor itself; firing endurance in
1865
- * {@link weaponsCapacitorMetrics} separately applies the deployed state.
1866
- * @throws {RangeError} If any pip allocation is outside `[0, 4]` or not finite.
1867
- * @example
1868
- * ```ts
1869
- * import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
1870
- *
1871
- * declare const build: ShipLoadout;
1872
- * const distributor = build.distributorMetrics({
1873
- * systemsPips: 2,
1874
- * enginesPips: 2,
1875
- * weaponsPips: 2,
1876
- * });
1877
- * distributor?.engines.rechargeRate; // MJ/s
1878
- * ```
1879
- */
1880
- distributorMetrics(options?: DistributorOptions): DistributorMetrics | null;
1881
1242
  }
1882
1243
 
1883
- export { type ApplyBlueprintOptions, type AvailableBlueprint, type BuildCost, type BuildCredits, type BuildMass, type BuildWeaponMetrics, type DefenceOptions, type DistributorOptions, type EngineeringNormalizationCode, type EngineeringNormalizationResult, type EngineeringNormalizationUnchanged, type EngineeringNormalizationUnsupported, type EngineeringNormalized, type ExperimentalEffectMutationCode, type ExperimentalEffectMutationResult, type ExperimentalEffectUnchanged, type ExperimentalEffectUnsupported, type ExperimentalEffectUpdated, type FittedModule, type FittedWeaponMetrics, type FixedMountRepairResult, type ImmovableReason, type JumpOptions, type JumpRangeSummary, LoadoutEditError, type LoadoutEditErrorCode, type LoadoutExportOptions, type LoadoutImportOutcome, type LoadoutSlot, type MobilityOptions, ShipLoadout, type SlefExportOptions, type StandardLoad, type StandardLoadInputs, type WeaponsOptions };
1244
+ export { type ApplyBlueprintOptions, type AvailableBlueprint, type EngineeringNormalizationCode, type EngineeringNormalizationResult, type EngineeringNormalizationUnchanged, type EngineeringNormalizationUnsupported, type EngineeringNormalized, type ExperimentalEffectMutationCode, type ExperimentalEffectMutationResult, type ExperimentalEffectUnchanged, type ExperimentalEffectUnsupported, type ExperimentalEffectUpdated, type FittedModule, type FixedMountRepairResult, type ImmovableReason, LoadoutEditError, type LoadoutEditErrorCode, type LoadoutExportOptions, type LoadoutImportOutcome, type LoadoutSlot, ShipLoadout, type SlefExportOptions };