@elite-dangerous-almanac/core 0.1.0-beta.3 → 0.1.0-beta.4

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 (115) hide show
  1. package/PROVENANCE/i18n/SOURCES.md +88 -0
  2. package/PROVENANCE/ships/SOURCES.md +113 -13
  3. package/README.md +19 -3
  4. package/THIRD_PARTY_NOTICES.md +38 -13
  5. package/dist/chunk-24INI4U7.js +1 -0
  6. package/dist/chunk-24INI4U7.js.map +1 -0
  7. package/dist/chunk-3IMWUYXJ.js +1 -0
  8. package/dist/{chunk-A3NM7SAJ.js.map → chunk-3IMWUYXJ.js.map} +1 -1
  9. package/dist/chunk-5W43EQEQ.js +1 -0
  10. package/dist/chunk-5W43EQEQ.js.map +1 -0
  11. package/dist/chunk-7MKYFCFF.js +1 -0
  12. package/dist/chunk-7MKYFCFF.js.map +1 -0
  13. package/dist/chunk-BUV3MXGK.js +1 -0
  14. package/dist/chunk-BUV3MXGK.js.map +1 -0
  15. package/dist/chunk-DHOCTGWB.js +1 -0
  16. package/dist/chunk-DHOCTGWB.js.map +1 -0
  17. package/dist/{chunk-XH7WDPS2.js → chunk-DMCFX6VO.js} +1 -1
  18. package/dist/chunk-DYFUHGJQ.js +1 -0
  19. package/dist/chunk-DYFUHGJQ.js.map +1 -0
  20. package/dist/chunk-ESEM3LPU.js +1 -0
  21. package/dist/chunk-ESEM3LPU.js.map +1 -0
  22. package/dist/chunk-HOKW2EWE.js +1 -0
  23. package/dist/chunk-HOKW2EWE.js.map +1 -0
  24. package/dist/{chunk-A6XPYSVR.js → chunk-IHHS5Q5S.js} +1 -1
  25. package/dist/{chunk-A6XPYSVR.js.map → chunk-IHHS5Q5S.js.map} +1 -1
  26. package/dist/chunk-IJRKEODR.js +1 -0
  27. package/dist/{chunk-NEVMA6RJ.js.map → chunk-IJRKEODR.js.map} +1 -1
  28. package/dist/chunk-KPETDYQX.js +1 -0
  29. package/dist/chunk-KPETDYQX.js.map +1 -0
  30. package/dist/{chunk-Q22LXT53.js → chunk-NQ2ID6BF.js} +1 -1
  31. package/dist/chunk-NSSWYRHN.js +1 -0
  32. package/dist/{chunk-Y2MUIE5W.js.map → chunk-NSSWYRHN.js.map} +1 -1
  33. package/dist/chunk-O72KI7RP.js +1 -0
  34. package/dist/{chunk-K3AMV27L.js.map → chunk-O72KI7RP.js.map} +1 -1
  35. package/dist/{chunk-INNFH37U.js → chunk-PDE4YOFT.js} +1 -1
  36. package/dist/{chunk-INNFH37U.js.map → chunk-PDE4YOFT.js.map} +1 -1
  37. package/dist/chunk-QMQHIZGS.js +1 -0
  38. package/dist/chunk-QMQHIZGS.js.map +1 -0
  39. package/dist/chunk-RF2IKLWQ.js +1 -0
  40. package/dist/chunk-RF2IKLWQ.js.map +1 -0
  41. package/dist/chunk-T24FTOEU.js +1 -0
  42. package/dist/{chunk-3ZX45CZJ.js.map → chunk-T24FTOEU.js.map} +1 -1
  43. package/dist/chunk-TLA6LHLN.js +1 -0
  44. package/dist/{chunk-V4C6FIE2.js.map → chunk-TLA6LHLN.js.map} +1 -1
  45. package/dist/chunk-TXT2LOZI.js +1 -0
  46. package/dist/chunk-TXT2LOZI.js.map +1 -0
  47. package/dist/{chunk-LFVHJT6D.js → chunk-VQQ6WHAL.js} +1 -1
  48. package/dist/{chunk-LFVHJT6D.js.map → chunk-VQQ6WHAL.js.map} +1 -1
  49. package/dist/i18n/blueprints.d.ts +30 -0
  50. package/dist/i18n/blueprints.js +1 -0
  51. package/dist/i18n/blueprints.js.map +1 -0
  52. package/dist/i18n/experimental-effects.d.ts +30 -0
  53. package/dist/i18n/experimental-effects.js +1 -0
  54. package/dist/i18n/experimental-effects.js.map +1 -0
  55. package/dist/i18n/index.d.ts +7 -0
  56. package/dist/i18n/index.js +1 -0
  57. package/dist/i18n/index.js.map +1 -0
  58. package/dist/i18n/modules.d.ts +32 -0
  59. package/dist/i18n/modules.js +1 -0
  60. package/dist/i18n/modules.js.map +1 -0
  61. package/dist/materials/index.js +1 -1
  62. package/dist/materials/materials-all.js +1 -1
  63. package/dist/materials/materials.js +1 -1
  64. package/dist/{ship-loadout-CWpORI0v.d.ts → ship-loadout-BAU7scrE.d.ts} +207 -12
  65. package/dist/ships/heat.d.ts +383 -0
  66. package/dist/ships/heat.js +1 -0
  67. package/dist/ships/heat.js.map +1 -0
  68. package/dist/ships/index.d.ts +11 -6
  69. package/dist/ships/index.js +1 -1
  70. package/dist/ships/loadout-calculations.d.ts +2 -0
  71. package/dist/ships/loadout-calculations.js +1 -1
  72. package/dist/ships/loadout-validation.d.ts +29 -2
  73. package/dist/ships/loadout-validation.js +1 -1
  74. package/dist/ships/mobility.d.ts +139 -0
  75. package/dist/ships/mobility.js +1 -0
  76. package/dist/ships/mobility.js.map +1 -0
  77. package/dist/ships/module-limits.d.ts +79 -0
  78. package/dist/ships/module-limits.js +1 -0
  79. package/dist/ships/module-limits.js.map +1 -0
  80. package/dist/ships/modules-all.js +1 -1
  81. package/dist/ships/modules-core.js +1 -1
  82. package/dist/ships/modules-hardpoint.js +1 -1
  83. package/dist/ships/modules-internal.js +1 -1
  84. package/dist/ships/modules-utility.js +1 -1
  85. package/dist/ships/modules.d.ts +55 -2
  86. package/dist/ships/modules.js +1 -1
  87. package/dist/ships/pre-engineered-stats.js +1 -1
  88. package/dist/ships/shield-recovery.d.ts +116 -0
  89. package/dist/ships/shield-recovery.js +1 -0
  90. package/dist/ships/shield-recovery.js.map +1 -0
  91. package/dist/ships/ship-loadout.d.ts +4 -1
  92. package/dist/ships/ship-loadout.js +1 -1
  93. package/dist/ships/ships.d.ts +49 -21
  94. package/dist/ships/ships.js +1 -1
  95. package/dist/ships/slef.d.ts +7 -1
  96. package/dist/ships/slef.js +1 -1
  97. package/dist/ships/weapons.d.ts +10 -0
  98. package/dist/ships/weapons.js +1 -1
  99. package/package.json +36 -3
  100. package/dist/chunk-3ZX45CZJ.js +0 -1
  101. package/dist/chunk-A3NM7SAJ.js +0 -1
  102. package/dist/chunk-COE5ADYJ.js +0 -1
  103. package/dist/chunk-COE5ADYJ.js.map +0 -1
  104. package/dist/chunk-K3AMV27L.js +0 -1
  105. package/dist/chunk-NEVMA6RJ.js +0 -1
  106. package/dist/chunk-PP2VSA6M.js +0 -1
  107. package/dist/chunk-PP2VSA6M.js.map +0 -1
  108. package/dist/chunk-R4OD62HV.js +0 -1
  109. package/dist/chunk-R4OD62HV.js.map +0 -1
  110. package/dist/chunk-V4C6FIE2.js +0 -1
  111. package/dist/chunk-Y2DXFWOH.js +0 -1
  112. package/dist/chunk-Y2DXFWOH.js.map +0 -1
  113. package/dist/chunk-Y2MUIE5W.js +0 -1
  114. /package/dist/{chunk-XH7WDPS2.js.map → chunk-DMCFX6VO.js.map} +0 -0
  115. /package/dist/{chunk-Q22LXT53.js.map → chunk-NQ2ID6BF.js.map} +0 -0
@@ -0,0 +1,88 @@
1
+ # Localized game-name catalogues
2
+
3
+ These catalogues keep the canonical English display names already published by the
4
+ ships data domain and add only localized values carried explicitly by the pinned community
5
+ sources. They are deliberately sparse: absence means that no accepted source carried a
6
+ value, and the lookup API returns `null` for that locale rather than treating English as
7
+ a translation.
8
+
9
+ ## `module-names.jsonc`
10
+
11
+ - **Acquired:** 2026-08-14 UTC.
12
+ - **EDDI revision:** commit `fdc1f47933bd930464610111fa11fc9dae264414`.
13
+ - **EDSY revision:** commit `e446fbe6e4597dea7ab0bd3105b9a36642388040`;
14
+ database version `424009901`, last-modified marker `20260810`.
15
+ - **Derivation:** all 1,199 current module symbols and English names come from the four
16
+ `data/ships/modules-*.jsonc` catalogues. EDDI's `ModuleDefinitions.cs` joins a symbol
17
+ to a resource key; `Properties/Modules.resx` and its localized siblings supply names.
18
+ An EDDI value is accepted only when its base English resource exactly equals the
19
+ Almanac's canonical English name. This prevents broad EDDI families such as
20
+ `CargoRack`, `MissileRack` and `PlanetaryApproachSuite` from erasing the distinct Mk II,
21
+ seeker and advanced names the Almanac carries. EDSY's module `fdname` and effective
22
+ `namekey` fill a missing `de`, `es`, `fr`, `pt` or `ru` value only when EDSY's English
23
+ module name also exactly equals the canonical name. Values shared by modules with one
24
+ canonical name are retained only when they agree, then stored once under that name.
25
+ - **Coverage:** English covers all 1,199 symbols. The explicit translations cover 1,120
26
+ in German, 1,090 in Spanish, 1,088 in French, 767 in Hungarian, 1,028 in Italian,
27
+ 822 in Portuguese, 1,029 in Brazilian Portuguese, 1,153 in Russian and 1,028 in
28
+ Simplified Chinese.
29
+ - **Manual corrections:** none.
30
+
31
+ ## `blueprint-names.jsonc`
32
+
33
+ - **Acquired:** 2026-08-14 UTC.
34
+ - **EDSY revision:** commit `e446fbe6e4597dea7ab0bd3105b9a36642388040`;
35
+ database version `424009901`, last-modified marker `20260810`.
36
+ - **Derivation:** each of the 107 current keys and its English name comes from
37
+ `data/ships/blueprints.jsonc`. The Frontier `fdname` joins directly to EDSY's
38
+ `blueprint.fdname`; that record's id selects an explicit `blueprint-<id>` entry from
39
+ each language file. Names remain keyed per blueprint because a single English phrase
40
+ can have different grammatical translations for different module families.
41
+ - **Coverage:** English covers all 107 blueprints; EDSY covers 55 in German and 59 each
42
+ in Spanish, French, Portuguese and Russian. The remaining recipes include Operations
43
+ and other newer records EDSY does not identify or translate.
44
+ - **Manual corrections:** EDSY's French `FSD_FastBoot` value ends in one accidental
45
+ ASCII space; the stored display name removes it so a successful lookup is already
46
+ trimmed like every other catalogue name.
47
+
48
+ ## `experimental-effect-names.jsonc`
49
+
50
+ - **Acquired:** 2026-08-14 UTC.
51
+ - **EDSY revision:** commit `e446fbe6e4597dea7ab0bd3105b9a36642388040`;
52
+ database version `424009901`, last-modified marker `20260810`.
53
+ - **Derivation:** each of the 86 current keys and its English name comes from
54
+ `data/ships/experimental-effects.jsonc`. The Frontier `fdname` joins directly to
55
+ EDSY's `expeffect.fdname`; that record's id selects an explicit `expeffect-<id>` entry
56
+ from each language file.
57
+ - **Coverage:** English covers all 86 effects; EDSY covers 84 in German and 85 each in
58
+ Spanish, French, Portuguese and Russian.
59
+ - **Manual corrections:** none.
60
+
61
+ ## Deliberate absence: ship names
62
+
63
+ - **Acquired:** 2026-08-14 UTC.
64
+ - **Odyssey Materials Helper revision:** commit
65
+ `23343c453938e724f317c56e9eb7db0dbfa71f78`;
66
+ `application/src/main/resources/locale/ships/ships.csv` has SHA-256
67
+ `6785f7cea00f36ca0a853091fd88f8ed5481a60f55c269643bff1ab0c3d6993d`.
68
+ - **EDSY revision:** commit `e446fbe6e4597dea7ab0bd3105b9a36642388040`;
69
+ database version `424009901`, last-modified marker `20260810`.
70
+ - **Evaluation:** each of the 48 current ship symbols is joined to Odyssey Materials
71
+ Helper's `ships.name.*` key after removing punctuation and folding case. EDSY's
72
+ `eddb.js` `ship.fdname` supplies the second join, with explicit `ship-<id>` language
73
+ entries filling locales absent from the CSV. The resulting source-backed coverage is
74
+ 44 ships in Spanish, 42 in French, 38 in Portuguese, 45 in Russian and 44 in Chinese;
75
+ no accepted source carries a German ship-name table.
76
+ - **Standing conclusion:** all 213 resulting non-English values are byte-for-byte equal
77
+ to the canonical English ship names. A ship-name localization catalogue would
78
+ therefore publish no translated information and is deliberately absent. A source
79
+ carrying at least one genuinely localized ship name would justify revisiting this
80
+ conclusion.
81
+ - **Manual corrections:** none.
82
+
83
+ ## Known gaps
84
+
85
+ Localized coverage follows the pinned sources and is not complete for every catalogue or
86
+ locale. Missing values are observable as `null`, never an English fallback. Expanding
87
+ source-backed coverage, localized slot labels and higher-level generated text remains
88
+ tracked by [#245](https://github.com/DarkSession/Elite-Dangerous-Almanac/issues/245).
@@ -15,7 +15,7 @@ Referred to throughout by source name; the pin is here, once.
15
15
  | Odyssey Materials Helper CAPI fixture `application/src/test/resources/parser/capifc/test9.json` | commit `2c652a2349b754f1dde1a58b6daaac5a04e421a6` | 2026-08-09 UTC |
16
16
  | EDCD/Coriolis — the application, for its formulas | commit `68c042ca6e3db62372cbbb2077cf972345511712` | 2026-08-01 UTC |
17
17
  | msarilar/EDEngineer `EDEngineer/Resources/Data/blueprints.json` | SHA-256 `787e6bd0579264d7b4615a281318792cb212285786f4ae07f61ec1cc464cdec0` — read from the branch tip, so pinned by digest | 2026-08-08 UTC |
18
- | Elite Dangerous in-game verification | observed in-game | 2026-08-12 UTC |
18
+ | Elite Dangerous in-game verification | game version `4.4.0.3`; direct in-game observation | 2026-08-14 UTC |
19
19
 
20
20
  Every `eddb.js` derivation uses the baseline snapshot unless its catalogue note names
21
21
  the Vessel Hangar snapshot.
@@ -37,15 +37,84 @@ identity from FDevIDs, stats and slots from coriolis-data, joined on `symbol`.
37
37
  numeric ship-type `id` column is dropped — hulls are keyed by `symbol`.
38
38
  `entitlement` is FDevIDs' DLC/grant token, kept only where the CSV gives one (28 of
39
39
  the 48 hulls carry no entitlement, so the field is omitted rather than stored empty).
40
- - **Stats + slots source:** coriolis-data `ships/*.json` — `properties` for stats,
40
+ - **Stats + slots source:** coriolis-data `ships/*.json` — `properties` for manufacturer,
41
+ numeric size class and stats,
41
42
  `slots` + `bulkheads` for the layout.
43
+ - **Manufacturer and size derivation:** `properties.manufacturer` is copied, with
44
+ coriolis-data's abbreviated `Lakon` normalized to the shipyard name `Lakon Spaceways`;
45
+ numeric `properties.class` maps `1`/`2`/`3` to `small`/`medium`/`large`, the game's
46
+ landing-pad classes. The Lynx Highliner instead takes Zorgon Peterson and class 2
47
+ (`medium`) from Frontier's update notes and EDSY's `class:2` record.
42
48
  - **Stats derivation:** acquisition normalization looks up each hull's coriolis
43
49
  record by display name (normalized; coriolis "Viper" ⇒ registry "Viper MkIII") and
44
- copies a fixed whitelist of `properties` fields (`hullMass`, `speed`, `boost`,
45
- `baseArmour`, …). The repository's
50
+ copies a fixed whitelist of `properties` fields (`hullMass`, source `speed` as
51
+ `maximumSpeed`, `boost`, `baseArmour`, …). The repository's
46
52
  `scripts/data/ships/merge-normalized-catalogues.mjs` then performs the deterministic
47
53
  symbol join, preserving registry order and rejecting duplicate or unmatched input.
48
- Masses are tonnes, speeds m/s, rotation rates deg/s.
54
+ Masses are tonnes, speeds m/s, rotation rates deg/s. The in-game hull audit below
55
+ overrides registry values where they disagree.
56
+ - **Speed is stored as installed endpoints.** An Elite Dangerous `4.4.0.3` hull audit
57
+ recorded 2026-08-14 UTC supplies `minimumSpeed` and `maximumSpeed` directly for all 48
58
+ hulls.
59
+ The game's ratio values carry no independent information:
60
+ `minThrust = 100 * minimumSpeed / maximumSpeed` and
61
+ `pipSpeed = (maximumSpeed - minimumSpeed) / (4 * maximumSpeed)`. The ratios are
62
+ therefore not retained; reconstructing an endpoint from rounded ratios can disagree
63
+ with the installed whole-number value (the Lynx's `73.75%` ratio produces
64
+ `210.1875` m/s rather than its installed `210` m/s).
65
+ - **Angular rates are stored as zero- and four-ENG-PIP endpoints.** The same game audit
66
+ supplies `minPitch`, `minRoll` and `minYaw` from each flight-default block. The
67
+ selected full-rate words come from the hull overrides; an exact `-1.0` override
68
+ sentinel selects the corresponding flight-default word. This structure holds for all
69
+ 48 player hulls. EDSY's public handling implementation independently identifies these
70
+ minima as zero-ENG-PIP values and linearly interpolates each axis to its full rate.
71
+ - **Installed English display names are taken verbatim from the game.** The Elite
72
+ Dangerous `4.4.0.3` localisation audit recorded 2026-08-14 UTC replaces eight compact
73
+ registry spellings:
74
+
75
+ | Symbol | Registry name | Stored game name |
76
+ | ------------- | ---------------------- | ----------------------- |
77
+ | `Viper` | `Viper MkIII` | `Viper Mk III` |
78
+ | `CobraMkIII` | `Cobra MkIII` | `Cobra Mk III` |
79
+ | `Viper_MkIV` | `Viper MkIV` | `Viper Mk IV` |
80
+ | `CobraMkIV` | `Cobra MkIV` | `Cobra Mk IV` |
81
+ | `Krait_MkII` | `Krait MkII` | `Krait Mk II` |
82
+ | `Python_NX` | `Python MkII` | `Python Mk II` |
83
+ | `CobraMkV` | `Cobra MkV` | `Cobra Mk V` |
84
+ | `PantherMkII` | `Panther Clipper MkII` | `Panther Clipper Mk II` |
85
+
86
+ The same display-name corrections are propagated to the matching armour-module
87
+ `ship` foreign keys in `modules-core.jsonc`.
88
+
89
+ - **In-game hull-stat audit corrections.** Live game readings from Elite Dangerous
90
+ `4.4.0.3`, recorded 2026-08-14 UTC, govern the following registry disagreements and
91
+ omissions. Values are transcribed directly, subject only to the established numeric
92
+ precision described above.
93
+
94
+ | Field | Registry value → stored game value |
95
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
+ | `baseShieldStrength` | `Type7` 155→156 |
97
+ | `masslock` | `Orca` 16→15; `Cutter` 27→26; `Viper_MkIV` 7→8; `Asp_Scout` 8→9; `Mandalay` 11→12; `PantherMkII` 27→25; `LakonMiner` 16→15; `Explorer_NX` 19→21; `SmallCombat01_NX` 10→11; `MediumTransport01` absent→16 |
98
+ | `reserveFuelCapacity` | `Mandalay` 0.52→0.5; `PantherMkII` 1.16→1.11; `LakonMiner` 0.77→0.6; `SmallCombat01_NX` 0.57→0.61 |
99
+ | `heatCapacity` | `Dolphin` 165→245; `Python_NX` 316→260; `Type8` 226→236; `Mandalay` 250→245; `Corsair` 230→280; `PantherMkII` 250→329; `LakonMiner` 289→300; `Explorer_NX` 250→341; `SmallCombat01_NX` 237→263; `MediumTransport01` absent→279 |
100
+ | `heatDissipation` | `Python_NX` 52→52.05; `Type8` 36.5→36.45; `CobraMkV` 40.5→40.63; `Corsair` 52→52.05; `PantherMkII` 62.5→62.45; `LakonMiner` 52.15→52.05; `Explorer_NX` 72.1→71.93; `SmallCombat01_NX` 39.85→37.93; `MediumTransport01` absent→49.35 |
101
+ | `maximumSpeed` | `Type9_Military` 179→180; `TypeX_3` 204→200; `CobraMkV` 291→290; `PantherMkII` 181→180; `LakonMiner` 272→270; `SmallCombat01_NX` 271→270 |
102
+ | `boost` | `Type9_Military` 219→220; `CobraMkV` 412→410; `LakonMiner` 367→365 |
103
+ | `pitch` | `Type9_Military` 20→22; `Krait_MkII` 26→31; `TypeX` 39→38; `TypeX_3` 32→35; `Krait_Light` 26→31; `Mamba` 27→30; `Python_NX` 37.72→37; `CobraMkV` 45.61→45; `SmallCombat01_NX` 51.4→50 |
104
+ | `roll` | `Type9_Military` 20→40; `TypeX` 92→90; `Mamba` 80→75; `Python_NX` 92.76→91; `CobraMkV` 121.62→120; `SmallCombat01_NX` 123.36→120 |
105
+ | `yaw` | `Python_NX` 12.74→12.5; `CobraMkV` 33.45→33; `SmallCombat01_NX` 24.67→24 |
106
+
107
+ - **`heatDissipation` is the hull's audited maximum cooling rate.** Coriolis-data carries no
108
+ dissipation figure at all, and the game shows a player none: it is a community
109
+ measurement of Frontier's game rather than a stat the game displays. The baseline is
110
+ EDSY `eddb.js` `ship[…].heatdismax` from the pinned snapshot, joined by
111
+ case-insensitive `fdname` / `symbol`. A complete maintainer-supplied thermal audit
112
+ from Elite Dangerous `4.4.0.3`, recorded 2026-08-14 UTC, governs all 48 values and
113
+ supplies the nine corrections and omissions listed above. The other 39 values agree
114
+ with EDSY exactly.
115
+ Each value is the load in thermal-load units per second shed at heat level 1. The
116
+ stored catalogue range is Hauler `16.2` through Cutter `72.58`.
117
+
49
118
  - **Slots derivation:** coriolis's fixed-order `slots.standard` seven-array becomes
50
119
  the seven named `core` sizes (power plant, thrusters, frame shift drive, life
51
120
  support, power distributor, sensors, fuel tank); `slots.hardpoints` splits into
@@ -223,17 +292,20 @@ slots are outside the hull layout.
223
292
  Inara's ship page 68, read 2026-08-02 UTC, listing
224
293
  1 Large Mining, 1 Medium, 2 Medium Mining, 3 Small and 1 Small Mining. The four
225
294
  unrestricted mounts are exactly the `[2, 1, 1, 1]` the record already had.
226
- - **Lynx Highliner (`MediumTransport01`) — from EDSY, Frontier's Lynx update notes and
227
- a Frontier journal capture:**
295
+ - **Lynx Highliner (`MediumTransport01`) — from EDSY, Frontier's Lynx update notes,
296
+ a Frontier journal capture and in-game verification:**
228
297
  the Lynx has no coriolis hull entry, so its stats and slot layout are sourced instead
229
298
  from EDSY's ship data and Frontier's Lynx update notes (hull mass 260 t, 285/350 m/s,
230
299
  200/350 base shield/armour, hardness 55, 2 crew, rotation 26/60/19 deg/s, min thrust
231
300
  73.75%; core PP5/thr6/FSD5/LS6/dist5/sen3/tank5; hardpoints 1 large + 4 medium;
232
301
  4 utilities; unrestricted/passenger optionals 6/6/6/5/5/4/4/3/2/1; its five armour
233
302
  options at 0/26/53/53/53 t, carried on the `MediumTransport01_Armour_*` module
234
- records). Values
235
- the static catalogue does not expose are omitted rather than invented: `masslock`,
236
- `heatCapacity`, `pipSpeed`, acceleration, and the min-pitch / boost-energy figures.
303
+ records). EDSY independently gives the 23 deg/s zero-ENG-PIP pitch rate. A maintainer's
304
+ Elite Dangerous `4.4.0.3` game audit recorded 2026-08-14 UTC supplies `masslock: 16`,
305
+ `heatCapacity: 279`, `heatDissipation: 49.35`, the installed `minimumSpeed: 210` /
306
+ `maximumSpeed: 285` pair, and the complete angular endpoints `23/26` pitch, `60/60`
307
+ roll and `19/19` yaw. Values the static catalogue does not expose are omitted rather
308
+ than invented: acceleration and the boost-energy figures.
237
309
  Its two size-6 and one size-5 passenger mounts carry `"restriction": "passenger"` and
238
310
  the names `Passenger01`–`Passenger03`, sourced above. A final size-1
239
311
  `planetaryApproachSuite` mount named `PlanetaryApproachSuite` comes directly from
@@ -603,7 +675,7 @@ account for 300 fields on 135 modules in addition to the Resource Siphon:
603
675
  | Records | Fields | Stored in-game values |
604
676
  | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------ |
605
677
  | `Int_Engine_Size4_Class{2,4}`, `Int_Hyperdrive_Size4_Class4` | thruster min/max mass; FSD optimal mass | 158/473, 193/578 and 438 t |
606
- | `Int_Engine_Size{2,3}_Class5_Fast` | optimal/maximum multiplier | 1.1 / 1.2 on both |
678
+ | `Int_Engine_Size{2,3}_Class5_Fast` | acceleration optimal/maximum multiplier | 1.1 / 1.2 on both |
607
679
  | `Int_GuardianShieldReinforcement_Size{1..5}_Class{1,2}` | integrity | 36/42, 40/48, 45/55, 51/63, 58/72 |
608
680
  | `Int_MetaAlloyHullReinforcement_Size{1..5}_Class{1,2}` | caustic resistance | 0.02 on all ten |
609
681
  | shield generators | regeneration / broken regeneration | observed 1.06–5.76 values per symbol |
@@ -615,8 +687,11 @@ account for 300 fields on 135 modules in addition to the Resource Siphon:
615
687
  | AX missiles, subsurface displacement missiles and seismic charge launchers | projectile boundary parameters; misleading ordinary ranges absent | ten records and 16 absences |
616
688
 
617
689
  In-game verification gives the integer thruster/FSD masses, 1.1/1.2 enhanced-thruster
618
- multipliers and the rising Guardian Shield Reinforcement integrity ladder. These values
619
- take precedence over family-shaped inference and registry agreement.
690
+ acceleration multipliers and the rising Guardian Shield Reinforcement integrity ladder.
691
+ The enhanced thrusters additionally retain Coriolis's separately sourced 0.9/1.25/1.6
692
+ speed curve and 0.9/1.1/1.3 rotation curve; its mobility calculation applies those
693
+ curves instead of treating the outfitting panel's acceleration curve as all three.
694
+ These values take precedence over family-shaped inference and registry agreement.
620
695
 
621
696
  **The two fixed Guardian Shard Cannons' damage is derived from a panel reading, not read
622
697
  off one.** Individual outfitting panels observed **2026-08-10 UTC**, with grade-1
@@ -819,6 +894,31 @@ and bulkhead name because those records carry no symbol upstream.
819
894
  restriction, so it is wrong on anything the game also sells for an ordinary
820
895
  optional: a plain cargo rack fits a `cargo` mount _and_ every unrestricted one, and
821
896
  does not carry it. The set of five is pinned, so widening it is a deliberate act.
897
+ - **`exclusionGroup`** carries EDSY's one-per-ship `limit` families, renamed from its
898
+ compact ids to stable descriptive values. EDSY has 17 one-per-ship source families;
899
+ the stored set covers 194 internal and utility records in 16 of them: shield
900
+ generators (standard, bi-weave and prismatic
901
+ share one group), fuel scoops, refineries, frame-shift-drive interdictors, Guardian
902
+ FSD boosters, vessel hangars, docking computers, supercruise assist, multi-limpet
903
+ controllers, the two scanner families, experimental module stabilisers, and the five
904
+ one-per-ship utility families. EDSY sets every one of those limits to `1`. EDSY's
905
+ three legacy discovery scanners are absent because they
906
+ are not current outfitting records; `Int_SupercruiseAssist` matches EDSY's
907
+ `Int_SuperCruiseAssist` case-insensitively, as Frontier symbols are matched elsewhere.
908
+ The three Mk I bundle-granted and all six Mk II Vessel Hangars are absent from the
909
+ baseline table; they join to the separately pinned Vessel Hangar snapshot, where all
910
+ nine carry the same `limit:'ifh'` as the ordinary Mk I records.
911
+ - **`limitGroup` / `limitIncrease` preserve EDSY's non-exclusive fitting limit.** All
912
+ 33 current hardpoint records whose EDSY module carries `limit:'hex'` store
913
+ `limitGroup:'experimentalWeapon'`; the source's `eddb.limit.hex` is `4`. This is the
914
+ shared AX/Guardian experimental-weapon allowance, not a name-based classification:
915
+ the marked set is exactly the source set, including the Caustic Missile Rack and
916
+ excluding the two Guardian Nanite Torpedo Pylons, which EDSY does not limit. The
917
+ class-3 and class-5 Experimental Weapon Stabilisers carry EDSY's
918
+ `unlimit:'hex'` / `unlimitcount:1` and `2` as `limitIncrease`. EDSY's fitting logic
919
+ adds those increases to the base allowance and counts every fitted stabiliser; both
920
+ source records have effective `powerDraw:0`, so power state does not alter the
921
+ structural allowance.
822
922
  - **Pre-engineered/duplicate drives share a `symbol`** in coriolis (e.g. the V1
823
923
  FSDs); the first (primary) occurrence wins, and any baked engineering is expected
824
924
  to arrive as SLEF `Engineering.Modifiers` instead.
package/README.md CHANGED
@@ -24,7 +24,7 @@ Use a feature barrel when a bundler will tree-shake it:
24
24
  import { ProceduralSystem } from '@elite-dangerous-almanac/core/astro';
25
25
  ```
26
26
 
27
- There is no package-wide root entry; choose one of the five feature areas or a leaf.
27
+ There is no package-wide root entry; choose one of the six feature areas or a leaf.
28
28
 
29
29
  Use leaf subpaths to avoid evaluating unrelated data modules in native ESM:
30
30
 
@@ -36,11 +36,12 @@ import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
36
36
  The heavyweight module registries, planetary/combined nebula catalogues and codex-region
37
37
  coordinate lookup are only exported from their leaf subpaths, not the feature barrels.
38
38
 
39
- The package has five feature areas:
39
+ The package has six feature areas:
40
40
 
41
41
  - `astro`: procedural names, id64 addresses, regions, nebulae and permit locks;
42
42
  - `ships`: ships, modules, SLEF loadouts, engineering and build metrics;
43
43
  - `equipment`: Odyssey suits, handheld weapons, grade upgrades and modifications;
44
+ - `i18n`: sparse localized module, blueprint and experimental-effect names;
44
45
  - `materials`: ship engineering materials and Odyssey micro resources;
45
46
  - `commodities`: standard and rare market goods.
46
47
 
@@ -92,7 +93,7 @@ build.toSlefString({
92
93
  `ShipLoadout` imports every ship and module catalogue so it can resolve any build.
93
94
  When only one calculation is required, use the data-free leaf modules under
94
95
  `ships/jump-range`, `ships/power`, `ships/shields`, `ships/armour`, `ships/weapons`,
95
- `ships/ammunition` or `ships/resistances`.
96
+ `ships/ammunition`, `ships/heat` or `ships/resistances`.
96
97
 
97
98
  `build.validation` reports validity and operational completeness. Potentially incomplete
98
99
  aggregates are nullable and have a diagnostic counterpart (`cargoCapacityResult`,
@@ -112,6 +113,21 @@ getCommodityByName('lavian brandy')?.rare; // -> true
112
113
  getPersonalWeaponByName('Karma AR-50')?.grades['5'].damage; // -> 2.8
113
114
  ```
114
115
 
116
+ Localized game names live on their own subpath, so applications that use only English
117
+ do not bundle them. English names are complete; other locales return `null` where the
118
+ pinned sources carry no translation instead of silently substituting English:
119
+
120
+ ```ts
121
+ import { getBlueprintName, getModuleName } from '@elite-dangerous-almanac/core/i18n';
122
+
123
+ getModuleName('Int_Hyperdrive_Size6_Class5', 'de-DE'); // -> "Frameshiftantrieb"
124
+ getBlueprintName('FSD_LongRange', 'fr-FR'); // -> "Portée FSD améliorée"
125
+ ```
126
+
127
+ Unqualified `zh` selects the source's Simplified Chinese (`zh-CN`) module names.
128
+ Explicitly different Chinese scripts or regions such as `zh-TW` do not fall back to
129
+ Simplified Chinese and return `null` unless a matching source is added.
130
+
115
131
  Registry lookups ignore case and surrounding whitespace. Material, commodity and
116
132
  module lookups search their complete registry by default and accept an optional
117
133
  catalogue to narrow the results. Nebula queries require an explicit catalogue so the
@@ -29,15 +29,30 @@ a verbatim copy produced at build time — edit this file, never the copy.
29
29
  - **[EDCD/Coriolis](https://github.com/EDCD/coriolis)** — the Coriolis _application_, by
30
30
  the **Coriolis contributors**, whose code is **MIT**-licensed. (Its data repository is a
31
31
  separate credit, under Data below.) The **build-metric algorithms** are ported as fact
32
- (our own implementation) from it: the power budget and its priority groups, shield
33
- strength and its mass curve, armour hit points, resistance stacking with its diminishing
34
- returns, and weapon DPS / capacitor draw / heat. It credits the original Frontier-forum
35
- research the formulas come from.
36
- - **The jump-range and fuel algorithm**, the **engineered ammunition rounding** rule and
37
- the **build metrics above** are cross-checked against, or ported as fact from, EDSY
38
- (credited under Data below). The jump-range model derives from Frontier's "mass effect on
39
- hyperspace range" description; Coriolis carries the same maths for the metrics, and a clip
40
- round-up that omits EDSY's burst step, where EDSY is followed.
32
+ (our own implementation) from it: the power budget and its priority groups; loaded speed,
33
+ boost and rotation through the thruster-mass and ENG-pip curves; shield strength and its
34
+ mass curve; shield collapse delay, regeneration and SYS-capacitor recovery timing; armour
35
+ hit points; resistance stacking with its diminishing returns; and weapon DPS / capacitor
36
+ draw / heat. It credits the original Frontier-forum research the formulas come from.
37
+ - **The heat model** the equilibrium heat level a thermal load settles at, how heat
38
+ moves towards it over time, and the multiplier a drained weapons capacitor puts on a
39
+ weapon's thermal load is ported as fact from EDSY's `edsy.js`
40
+ (`getEquilibriumHeatLevel`, `getTimeUntilHeatLevel`, `getHeatLevelAtTime`,
41
+ `getEffectiveWeaponThermalLoad`, `updateUIStatsThm`), which credits the Frontier-forum
42
+ research thread
43
+ [Research: detailed heat mechanics](https://forums.frontier.co.uk/threads/research-detailed-heat-mechanics.286628/)
44
+ the mechanics were reverse-engineered in. Frontier publishes no heat formula and
45
+ coriolis-data models none, so the model is community reverse-engineering, and the
46
+ per-hull dissipation figures it reads (credited with EDSY's other data and the
47
+ running-game audits below) are community _measurements_ of the game rather than stats
48
+ the game displays. Both
49
+ describe Frontier's game and fall under the same Frontier notice as every other stat
50
+ here; what differs is how they were arrived at, not who owns them.
51
+ - **The jump-range and fuel algorithm**, **per-axis ENG-pip handling**, the **engineered
52
+ ammunition rounding** rule and the **build metrics above** are cross-checked against, or
53
+ ported as fact from, EDSY (credited under Data below). The jump-range model derives from
54
+ Frontier's "mass effect on hyperspace range" description; Coriolis carries the same maths
55
+ for the metrics, and a clip round-up that omits EDSY's burst step, where EDSY is followed.
41
56
  - **Galactic codex region lookup** — resolving a region from galactic coordinates or from a
42
57
  boxel, ported as fact (our own implementation) from `RegionMap.js` in
43
58
  EliteDangerousRegionMap, whose region tables are credited under Data below.
@@ -61,8 +76,17 @@ a verbatim copy produced at build time — edit this file, never the copy.
61
76
  `eddb.js` and `edsy.js`. Supplies the values coriolis-data leaves blank or carries
62
77
  wrongly (module mass, integrity, power draw, boot time, the base stats engineering
63
78
  recipes scale), the experimental-effect modifiers and their recipes, the module-group
64
- engineering menus, the journal slot names, and the attribute-to-journal-Label mapping.
65
- The values are Elite Dangerous game data — see the Frontier notice below.
79
+ engineering menus, per-ship module-count limits and fitted stabiliser increases, the
80
+ baseline per-hull maximum heat dissipation, the
81
+ Lynx Highliner's hull/slot figures and zero-pip minimum pitch, the journal slot names,
82
+ localized module, blueprint and experimental-effect names, and the
83
+ attribute-to-journal-Label mapping. The values are Elite Dangerous game data — see the
84
+ Frontier notice below. Heat dissipation is the one entry the game never shows a player:
85
+ it is community measurement of Frontier's game, described under Algorithms above, and
86
+ carried here on the same terms as the rest.
87
+ - **[EDDI](https://github.com/EDCD/EDDI)** by the **EDDI contributors**, **Apache 2.0**
88
+ for its code — its module definitions and resource tables supply localized outfitting
89
+ names. The names are factual Elite Dangerous game data; no EDDI code is incorporated.
66
90
  - **[Inara](https://inara.cz/)** — the blueprint and outfitting registries behind the
67
91
  Operations pre-engineered blueprints and shop rows, the ship pages used to corroborate
68
92
  hull layouts, and the component pages that grade the Thargoid caustic / Titan materials
@@ -92,8 +116,8 @@ a verbatim copy produced at build time — edit this file, never the copy.
92
116
  **Jixxed**, **MIT** — supplies the personal suit and handheld-weapon identities, stats,
93
117
  journal symbols, grade-upgrade recipes and engineer-applied modification recipes. A
94
118
  CAPI response in its test resources also corroborates the six bundle-granted Vessel
95
- Hangar variants. The values and response are factual Elite Dangerous game data. Its
96
- MIT notice is reproduced below.
119
+ Hangar variants. The values and response are factual Elite Dangerous game data. Its MIT
120
+ notice is reproduced below.
97
121
  - **[msarilar/EDEngineer](https://github.com/msarilar/EDEngineer)**, **MIT** for its code
98
122
  — its `blueprints.json` corroborates which weapons the Overcharged recipe gives a clip
99
123
  penalty to. The values are game data.
@@ -160,6 +184,7 @@ derived and every manual correction are recorded with the data itself, in
160
184
  [`data/astro/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/astro/SOURCES.md),
161
185
  [`data/materials/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/materials/SOURCES.md),
162
186
  [`data/equipment/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/equipment/SOURCES.md),
187
+ [`data/i18n/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/i18n/SOURCES.md),
163
188
  [`data/ships/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/ships/SOURCES.md)
164
189
  and
165
190
  [`data/commodities/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/commodities/SOURCES.md),
@@ -0,0 +1 @@
1
+ var requireFiniteNonNegative=(scope,name,value)=>{if(!Number.isFinite(value)||value<0){throw new RangeError(`${scope}: ${name} must be a finite non-negative number`)}};var requireFiniteRange=(scope,name,value,minimum,maximum)=>{if(!Number.isFinite(value)||value<minimum||value>maximum){throw new RangeError(`${scope}: ${name} must be a finite number from ${minimum} to ${maximum}`)}};var validateCurve=(scope,curve)=>{for(const field of["minMass","optMass","maxMass"]){requireFiniteNonNegative(scope,field,curve[field])}const allMassesEqual=curve.minMass===curve.optMass&&curve.optMass===curve.maxMass;if(!allMassesEqual&&!(curve.minMass<curve.optMass&&curve.optMass<curve.maxMass)){throw new RangeError(`${scope}: masses must be strictly ordered minMass < optMass < maxMass, or all equal`)}for(const field of["minMultiplier","optMultiplier","maxMultiplier"]){requireFiniteNonNegative(scope,field,curve[field])}const allMultipliersEqual=curve.minMultiplier===curve.optMultiplier&&curve.optMultiplier===curve.maxMultiplier;const strictlyOrderedMultipliers=curve.minMultiplier<curve.optMultiplier&&curve.optMultiplier<curve.maxMultiplier;if(!allMultipliersEqual&&!strictlyOrderedMultipliers){throw new RangeError(`${scope}: multipliers must be strictly ordered minMultiplier < optMultiplier < maxMultiplier, or all equal`)}if(allMassesEqual&&!allMultipliersEqual){throw new RangeError(`${scope}: an all-equal mass curve must have equal multipliers`)}};var curveMultiplier=(scope,mass,thrusters)=>{requireFiniteNonNegative(scope,"mass",mass);validateCurve(`${scope}: thrusters`,thrusters);if(mass>thrusters.maxMass)return 0;const span=thrusters.maxMass-thrusters.minMass;if(span<=0||thrusters.maxMultiplier===thrusters.minMultiplier){return thrusters.optMultiplier}const normalised=Math.max(0,Math.min(1,(thrusters.maxMass-mass)/span));const optNormalised=Math.min(1,(thrusters.maxMass-thrusters.optMass)/span);const exponent=Math.log((thrusters.optMultiplier-thrusters.minMultiplier)/(thrusters.maxMultiplier-thrusters.minMultiplier))/Math.log(optNormalised);if(!Number.isFinite(exponent)){throw new RangeError(`${scope}: curve values do not produce a finite exponent`)}return thrusters.minMultiplier+Math.pow(normalised,exponent)*(thrusters.maxMultiplier-thrusters.minMultiplier)};function thrusterMassCurveMultiplier(mass,thrusters){return curveMultiplier("thrusterMassCurveMultiplier",mass,thrusters)}function mobilityMetrics(input){const pips=input.enginesPips??4;requireFiniteRange("mobilityMetrics","enginesPips",pips,0,4);for(const field of["minimumSpeed","maximumSpeed","boost","pitch","roll","yaw","mass"]){requireFiniteNonNegative("mobilityMetrics",field,input[field])}if(input.minimumSpeed>input.maximumSpeed){throw new RangeError("mobilityMetrics: minimumSpeed must not exceed maximumSpeed")}for(const[minimum,maximum]of[["minPitch","pitch"],["minRoll","roll"],["minYaw","yaw"]]){requireFiniteRange("mobilityMetrics",minimum,input[minimum],0,input[maximum])}if(!input.thrusters)return null;validateCurve("mobilityMetrics: thrusters",input.thrusters);const massCurveMultiplier=curveMultiplier("mobilityMetrics: speed curve",input.mass,input.thrusters.speedCurve??input.thrusters);const rotationMassCurveMultiplier=curveMultiplier("mobilityMetrics: rotation curve",input.mass,input.thrusters.rotationCurve??input.thrusters);const pipMultiplier=pips/4;const speedAtPips=input.minimumSpeed+(input.maximumSpeed-input.minimumSpeed)*pipMultiplier;const handlingAtPips=(maximum,minimum)=>minimum+(maximum-minimum)*pipMultiplier;return{speed:speedAtPips*massCurveMultiplier,boost:input.boost*massCurveMultiplier,pitch:handlingAtPips(input.pitch,input.minPitch)*rotationMassCurveMultiplier,roll:handlingAtPips(input.roll,input.minRoll)*rotationMassCurveMultiplier,yaw:handlingAtPips(input.yaw,input.minYaw)*rotationMassCurveMultiplier,massCurveMultiplier:massCurveMultiplier,rotationMassCurveMultiplier:rotationMassCurveMultiplier}}export{mobilityMetrics,thrusterMassCurveMultiplier};//# sourceMappingURL=chunk-24INI4U7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ships/mobility.ts"],"names":["requireFiniteNonNegative","scope","name","value","Number","isFinite","RangeError","requireFiniteRange","minimum","maximum","validateCurve","curve","field","allMassesEqual","minMass","optMass","maxMass","allMultipliersEqual","minMultiplier","optMultiplier","maxMultiplier","strictlyOrderedMultipliers","curveMultiplier","mass","thrusters","span","normalised","Math","max","min","optNormalised","exponent","log","pow","thrusterMassCurveMultiplier","mobilityMetrics","input","pips","enginesPips","minimumSpeed","maximumSpeed","massCurveMultiplier","speedCurve","rotationMassCurveMultiplier","rotationCurve","pipMultiplier","speedAtPips","handlingAtPips","speed","boost","pitch","minPitch","roll","minRoll","yaw","minYaw"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{buildModuleCatalogue}from"./chunk-E45EDCYH.js";var modules_utility_default=[{symbol:"Hpt_ChaffLauncher_Tiny",kind:"chaffLaunchers",name:"Chaff Launcher",class:0,rating:"I",mass:1.3,integrity:20,powerDraw:.2,bootTime:0,alwaysPowered:true,rateOfFire:1,burstInterval:1,clipSize:1,ammoMaximum:10,reloadTime:10,distributorDraw:4,thermalLoad:4,cost:8500},{symbol:"Hpt_ElectronicCountermeasure_Tiny",kind:"ecms",name:"Electronic Countermeasure",class:0,rating:"F",mass:1.3,integrity:20,powerDraw:.2,bootTime:0,reloadTime:10,thermalLoad:4,maximumRange:3e3,cost:12500},{symbol:"Hpt_HeatSinkLauncher_Turret_Tiny",kind:"heatSinkLaunchers",name:"Heat Sink Launcher",class:0,rating:"I",mass:1.3,integrity:45,powerDraw:.2,bootTime:0,alwaysPowered:true,rateOfFire:.2,burstInterval:5,clipSize:1,ammoMaximum:2,reloadTime:10,distributorDraw:2,cost:3500},{symbol:"Hpt_PlasmaPointDefence_Turret_Tiny",kind:"pointDefence",name:"Point Defence",class:0,rating:"I",mass:.5,integrity:30,powerDraw:.2,bootTime:0,alwaysPowered:true,damage:.2,damageDistribution:{kinetic:1},rateOfFire:10,burstInterval:.2,burstRounds:4,burstRateOfFire:15,clipSize:12,ammoMaximum:1e4,reloadTime:.4,thermalLoad:.07,maximumRange:2500,shotSpeed:1e3,jitter:1.5,cost:18550},{symbol:"Hpt_CargoScanner_Size0_Class1",kind:"manifestScanners",name:"Cargo Scanner",class:0,rating:"E",exclusionGroup:"cargoScanner",mass:1.3,integrity:32,powerDraw:.2,bootTime:3,scannerRange:2e3,scanAngle:15,scanTime:10,cost:13544},{symbol:"Hpt_CargoScanner_Size0_Class2",kind:"manifestScanners",name:"Cargo Scanner",class:0,rating:"D",exclusionGroup:"cargoScanner",mass:1.3,integrity:24,powerDraw:.4,bootTime:3,scannerRange:2500,scanAngle:15,scanTime:10,cost:40633},{symbol:"Hpt_CargoScanner_Size0_Class3",kind:"manifestScanners",name:"Cargo Scanner",class:0,rating:"C",exclusionGroup:"cargoScanner",mass:1.3,integrity:40,powerDraw:.8,bootTime:3,scannerRange:3e3,scanAngle:15,scanTime:10,cost:121899},{symbol:"Hpt_CargoScanner_Size0_Class4",kind:"manifestScanners",name:"Cargo Scanner",class:0,rating:"B",exclusionGroup:"cargoScanner",mass:1.3,integrity:56,powerDraw:1.6,bootTime:3,scannerRange:3500,scanAngle:15,scanTime:10,cost:365698},{symbol:"Hpt_CargoScanner_Size0_Class5",kind:"manifestScanners",name:"Cargo Scanner",class:0,rating:"A",exclusionGroup:"cargoScanner",mass:1.3,integrity:48,powerDraw:3.2,bootTime:3,scannerRange:4e3,scanAngle:15,scanTime:10,cost:1097095},{symbol:"Hpt_CloudScanner_Size0_Class1",kind:"wakeScanners",name:"Frame Shift Wake Scanner",class:0,rating:"E",exclusionGroup:"frameShiftWakeScanner",mass:1.3,integrity:32,powerDraw:.2,bootTime:1,scannerRange:2e3,scanAngle:15,scanTime:10,cost:13544},{symbol:"Hpt_CloudScanner_Size0_Class2",kind:"wakeScanners",name:"Frame Shift Wake Scanner",class:0,rating:"D",exclusionGroup:"frameShiftWakeScanner",mass:1.3,integrity:24,powerDraw:.4,bootTime:1,scannerRange:2500,scanAngle:15,scanTime:10,cost:40633},{symbol:"Hpt_CloudScanner_Size0_Class3",kind:"wakeScanners",name:"Frame Shift Wake Scanner",class:0,rating:"C",exclusionGroup:"frameShiftWakeScanner",mass:1.3,integrity:40,powerDraw:.8,bootTime:1,scannerRange:3e3,scanAngle:15,scanTime:10,cost:121899},{symbol:"Hpt_CloudScanner_Size0_Class4",kind:"wakeScanners",name:"Frame Shift Wake Scanner",class:0,rating:"B",exclusionGroup:"frameShiftWakeScanner",mass:1.3,integrity:56,powerDraw:1.6,bootTime:1,scannerRange:3500,scanAngle:15,scanTime:10,cost:365698},{symbol:"Hpt_CloudScanner_Size0_Class5",kind:"wakeScanners",name:"Frame Shift Wake Scanner",class:0,rating:"A",exclusionGroup:"frameShiftWakeScanner",mass:1.3,integrity:48,powerDraw:3.2,bootTime:1,scannerRange:4e3,scanAngle:15,scanTime:10,cost:1097095},{symbol:"Hpt_CrimeScanner_Size0_Class1",kind:"killWarrantScanners",name:"Kill Warrant Scanner",class:0,rating:"E",exclusionGroup:"killWarrantScanner",mass:1.3,integrity:32,powerDraw:.2,bootTime:2,scannerRange:2e3,scanAngle:15,scanTime:10,cost:13544},{symbol:"Hpt_CrimeScanner_Size0_Class2",kind:"killWarrantScanners",name:"Kill Warrant Scanner",class:0,rating:"D",exclusionGroup:"killWarrantScanner",mass:1.3,integrity:24,powerDraw:.4,bootTime:2,scannerRange:2500,scanAngle:15,scanTime:10,cost:40633},{symbol:"Hpt_CrimeScanner_Size0_Class3",kind:"killWarrantScanners",name:"Kill Warrant Scanner",class:0,rating:"C",exclusionGroup:"killWarrantScanner",mass:1.3,integrity:40,powerDraw:.8,bootTime:2,scannerRange:3e3,scanAngle:15,scanTime:10,cost:121899},{symbol:"Hpt_CrimeScanner_Size0_Class4",kind:"killWarrantScanners",name:"Kill Warrant Scanner",class:0,rating:"B",exclusionGroup:"killWarrantScanner",mass:1.3,integrity:56,powerDraw:1.6,bootTime:2,scannerRange:3500,scanAngle:15,scanTime:10,cost:365698},{symbol:"Hpt_CrimeScanner_Size0_Class5",kind:"killWarrantScanners",name:"Kill Warrant Scanner",class:0,rating:"A",exclusionGroup:"killWarrantScanner",mass:1.3,integrity:48,powerDraw:3.2,bootTime:2,scannerRange:4e3,scanAngle:15,scanTime:10,cost:1097095},{symbol:"Hpt_ShieldBooster_Size0_Class1",kind:"shieldBoosters",name:"Shield Booster",class:0,rating:"E",mass:.5,integrity:25,powerDraw:.2,bootTime:0,shieldBoost:.04,kineticResistance:0,thermalResistance:0,explosiveResistance:0,alwaysPowered:true,cost:1e4},{symbol:"Hpt_ShieldBooster_Size0_Class2",kind:"shieldBoosters",name:"Shield Booster",class:0,rating:"D",mass:1,integrity:35,powerDraw:.5,bootTime:0,shieldBoost:.08,kineticResistance:0,thermalResistance:0,explosiveResistance:0,alwaysPowered:true,cost:23e3},{symbol:"Hpt_ShieldBooster_Size0_Class3",kind:"shieldBoosters",name:"Shield Booster",class:0,rating:"C",mass:2,integrity:40,powerDraw:.7,bootTime:0,shieldBoost:.12,kineticResistance:0,thermalResistance:0,explosiveResistance:0,alwaysPowered:true,cost:53e3},{symbol:"Hpt_ShieldBooster_Size0_Class4",kind:"shieldBoosters",name:"Shield Booster",class:0,rating:"B",mass:3,integrity:45,powerDraw:1,bootTime:0,shieldBoost:.16,kineticResistance:0,thermalResistance:0,explosiveResistance:0,alwaysPowered:true,cost:122e3},{symbol:"Hpt_ShieldBooster_Size0_Class5",kind:"shieldBoosters",name:"Shield Booster",class:0,rating:"A",mass:3.5,integrity:48,powerDraw:1.2,bootTime:0,shieldBoost:.2,kineticResistance:0,thermalResistance:0,explosiveResistance:0,alwaysPowered:true,cost:281e3},{symbol:"Hpt_AntiUnknownShutdown_Tiny",name:"Shutdown Field Neutraliser",class:0,rating:"F",entitlement:"ELITE_HORIZONS_V_PLANETARY_LANDINGS",mass:1.3,integrity:35,powerDraw:.2,bootTime:0,alwaysPowered:true,maximumRange:3e3,cost:63e3},{symbol:"Hpt_XenoScanner_Basic_Tiny",name:"Xeno Scanner",class:0,rating:"E",exclusionGroup:"experimentalUtility",entitlement:"ELITE_HORIZONS_V_PLANETARY_LANDINGS",mass:1.3,integrity:56,powerDraw:.2,bootTime:2,scannerRange:500,scanAngle:23,scanTime:10,cost:365698},{symbol:"Hpt_XenoScannerMk2_Basic_Tiny",name:"Enhanced Xeno Scanner",class:0,rating:"C",exclusionGroup:"experimentalUtility",entitlement:"ELITE_HORIZONS_V_PLANETARY_LANDINGS",mass:1.3,integrity:56,powerDraw:.8,bootTime:2,scannerRange:2e3,scanAngle:23,scanTime:10,cost:745948},{symbol:"Hpt_MRAScanner_Size0_Class1",name:"Pulse Wave Analyser",class:0,rating:"E",exclusionGroup:"pulseWaveAnalyser",mass:1.3,integrity:32,powerDraw:.2,bootTime:3,scannerRange:12e3,scanAngle:15,scanTime:3,cost:13544},{symbol:"Hpt_MRAScanner_Size0_Class2",name:"Pulse Wave Analyser",class:0,rating:"D",exclusionGroup:"pulseWaveAnalyser",mass:1.3,integrity:24,powerDraw:.4,bootTime:3,scannerRange:15e3,scanAngle:15,scanTime:3,cost:40633},{symbol:"Hpt_MRAScanner_Size0_Class3",name:"Pulse Wave Analyser",class:0,rating:"C",exclusionGroup:"pulseWaveAnalyser",mass:1.3,integrity:40,powerDraw:.8,bootTime:3,scannerRange:18e3,scanAngle:15,scanTime:3,cost:121899},{symbol:"Hpt_MRAScanner_Size0_Class4",name:"Pulse Wave Analyser",class:0,rating:"B",exclusionGroup:"pulseWaveAnalyser",mass:1.3,integrity:56,powerDraw:1.6,bootTime:3,scannerRange:21e3,scanAngle:15,scanTime:3,cost:365698},{symbol:"Hpt_MRAScanner_Size0_Class5",name:"Pulse Wave Analyser",class:0,rating:"A",exclusionGroup:"pulseWaveAnalyser",mass:1.3,integrity:48,powerDraw:3.2,bootTime:3,scannerRange:24e3,scanAngle:15,scanTime:3,cost:1097095},{symbol:"Hpt_CausticSinkLauncher_Turret_Tiny",kind:"heatSinkLaunchers",name:"Caustic Sink Launcher",class:0,rating:"I",mass:1.7,integrity:45,powerDraw:.6,bootTime:0,alwaysPowered:true,rateOfFire:.2,burstInterval:5,clipSize:1,ammoMaximum:5,reloadTime:10,distributorDraw:2,cost:5e4},{symbol:"Hpt_AntiUnknownShutdown_Tiny_V2",name:"Thargoid Pulse Neutraliser",class:0,rating:"E",mass:3,integrity:70,powerDraw:.4,bootTime:0,alwaysPowered:true,maximumRange:0,cost:15e4},{symbol:"Hpt_XenoScanner_Advanced_Tiny",name:"Pulse Wave Xeno Scanner",class:0,rating:"C",exclusionGroup:"experimentalUtility",mass:3,integrity:100,powerDraw:1,bootTime:2,scannerRange:1e3,scanAngle:23,scanTime:10,cost:85e4}];var UTILITY_MODULES=buildModuleCatalogue(modules_utility_default,"utility");export{UTILITY_MODULES};//# sourceMappingURL=chunk-3IMWUYXJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ships/modules-utility.ts"],"names":["buildModuleCatalogue","modules_utility_default","UTILITY_MODULES"],"mappings":"O,mxPAqCO,IAAME,gBAA+CF,qBACxDC,wBACA,kBACJC"}
1
+ {"version":3,"sources":["../src/ships/modules-utility.ts"],"names":["buildModuleCatalogue","modules_utility_default","UTILITY_MODULES"],"mappings":"O,8jRAqCO,IAAME,gBAA+CF,qBACxDC,wBACA,kBACJC"}
@@ -0,0 +1 @@
1
+ import{calculateModuleLimits}from"./chunk-KPETDYQX.js";import{truncate}from"./chunk-B5TT26XE.js";function validateLoadout(input){const issues=[];const seen=/* @__PURE__ */new Map;for(const module of input.modules){const normalized=module.slot.toLowerCase();const previous=seen.get(normalized);if(previous!==void 0){issues.push({code:"duplicateSlot",severity:"error",slot:module.slot,symbol:module.symbol,params:{slot:module.slot,previousSlot:previous,symbol:module.symbol},message:`Slot ${truncate(module.slot)} occurs more than once (also ${truncate(previous)})`})}else{seen.set(normalized,module.slot)}}const exclusive=/* @__PURE__ */new Map;for(const module of input.modules){if(!module.exclusionGroup)continue;const previous=exclusive.get(module.exclusionGroup);if(previous){issues.push({code:"duplicateExclusiveModule",severity:"error",slot:module.slot,symbol:module.symbol,params:{exclusionGroup:module.exclusionGroup,slot:module.slot,symbol:module.symbol,previousSlot:previous.slot,previousSymbol:previous.symbol},message:`${truncate(module.slot)}: ${truncate(module.symbol)} conflicts with ${truncate(previous.symbol)} in ${truncate(previous.slot)} (${module.exclusionGroup} is limited to one per ship)`})}else{exclusive.set(module.exclusionGroup,module)}}for(const usage of calculateModuleLimits(input.modules)){if(usage.excess===0)continue;issues.push({code:"moduleLimitExceeded",severity:"error",params:{group:usage.group,count:usage.count,limit:usage.limit},message:`${usage.group} has ${usage.count} fitted modules but the ship allows ${usage.limit}`})}if(input.slots===null){issues.push({code:"unknownHull",severity:"incomplete",message:`No slot layout is known for hull ${truncate(input.shipSymbol)}`,params:{shipSymbol:input.shipSymbol}})}else{const knownSlots=new Map(input.slots.map(slot=>[slot.key.toLowerCase(),slot]));for(const module of input.modules){if(module.requiresKnownSlot!==false&&!knownSlots.has(module.slot.toLowerCase())){issues.push({code:"unknownSlot",severity:"error",slot:module.slot,symbol:module.symbol,message:`${truncate(module.slot)} is not a slot on ${truncate(input.shipSymbol)}`,params:{shipSymbol:input.shipSymbol,slot:module.slot,symbol:module.symbol}})}}for(const slot of input.slots){if((slot.kind==="core"||slot.kind==="armour")&&!seen.has(slot.key.toLowerCase())){issues.push({code:"missingRequiredSlot",severity:"incomplete",slot:slot.key,message:`${truncate(slot.key)} is required for an operational build`,params:{slot:slot.key}})}}}for(const module of input.modules){if(!module.known){issues.push({code:"unknownModule",severity:"incomplete",slot:module.slot,symbol:module.symbol,message:`${truncate(module.slot)}: ${truncate(module.symbol)} is not in the module catalogue`,params:{slot:module.slot,symbol:module.symbol}})}else if(module.fitError!==null){issues.push({code:"incompatibleModule",severity:"error",slot:module.slot,symbol:module.symbol,message:`${truncate(module.slot)}: ${truncate(module.symbol)} ${truncate(module.fitError)}`,params:{...module.fitParams,slot:module.slot,symbol:module.symbol,constraint:module.fitConstraint??"unknownConstraint"}})}}const frozen=Object.freeze(issues.map(issue=>Object.freeze({...issue,...issue.params?{params:Object.freeze(Object.fromEntries(Object.entries(issue.params).map(([key,value])=>[key,Array.isArray(value)?Object.freeze([...value]):value])))}:{}})));return Object.freeze({valid:!issues.some(issue=>issue.severity==="error"),complete:issues.length===0,issues:frozen})}export{validateLoadout};//# sourceMappingURL=chunk-5W43EQEQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ships/loadout-validation.ts"],"names":["calculateModuleLimits","truncate","validateLoadout","input","issues","seen","Map","module","modules","normalized","slot","toLowerCase","previous","get","push","code","severity","symbol","params","previousSlot","message","set","exclusive","exclusionGroup","previousSymbol","usage","excess","group","count","limit","slots","shipSymbol","knownSlots","map","key","requiresKnownSlot","has","kind","known","fitError","fitParams","constraint","fitConstraint","frozen","Object","freeze","issue","fromEntries","entries","value","Array","isArray","valid","some","complete","length"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{normalizeKey}from"./chunk-S4DBNX2B.js";import{truncate}from"./chunk-B5TT26XE.js";var SYNTHETIC_HEADER=Object.freeze({appName:"",appVersion:""});var isRecord=value=>typeof value==="object"&&value!==null&&!Array.isArray(value);var isOptionalString=value=>value===void 0||typeof value==="string";var isOptionalFiniteNumber=value=>value===void 0||typeof value==="number"&&Number.isFinite(value);var isOptionalNumberInRange=(value,minimum,maximum=Number.POSITIVE_INFINITY)=>value===void 0||typeof value==="number"&&Number.isFinite(value)&&value>=minimum&&value<=maximum;var isOptionalIntegerInRange=(value,minimum,maximum)=>{if(value===void 0)return true;return typeof value==="number"&&Number.isInteger(value)&&value>=minimum&&value<=maximum};var CONSTRAINT_MESSAGES={objectRequired:"must be an object",stringRequired:"must be a string",booleanRequired:"must be a boolean",arrayRequired:"must be an array",finiteNumberRequired:"must be a finite number",nonNegativeNumberRequired:"must be a non-negative number",priorityRange:"must be an integer from 0 to 4",engineeringLevelRange:"must be an integer from 1 to 5",unitInterval:"must be a number from 0 to 1",binaryInteger:"must be 0 or 1",versionRequired:"must be a string or finite number",loadoutEventRequired:'must be "Loadout"',validLoadoutRequired:"is not a valid Loadout event"};var invalid=(code,path,constraint)=>({code:code,path:path,constraint:constraint,reason:CONSTRAINT_MESSAGES[constraint]});function diagnoseEngineering(value,path){if(!isRecord(value))return invalid("invalidEngineering",path,"objectRequired");if(typeof value.BlueprintName!=="string")return invalid("invalidEngineering",`${path}.BlueprintName`,"stringRequired");if(!isOptionalIntegerInRange(value.Level,1,5)||value.Level===void 0)return invalid("invalidEngineering",`${path}.Level`,"engineeringLevelRange");if(!isOptionalNumberInRange(value.Quality,0,1)||value.Quality===void 0)return invalid("invalidEngineering",`${path}.Quality`,"unitInterval");if(!isOptionalString(value.ExperimentalEffect))return invalid("invalidEngineering",`${path}.ExperimentalEffect`,"stringRequired");if(!isOptionalString(value.ExperimentalEffect_Localised))return invalid("invalidEngineering",`${path}.ExperimentalEffect_Localised`,"stringRequired");if(value.Modifiers!==void 0){if(!Array.isArray(value.Modifiers))return invalid("invalidEngineering",`${path}.Modifiers`,"arrayRequired");for(let index=0;index<value.Modifiers.length;index+=1){const modifier=value.Modifiers[index];const modifierPath=`${path}.Modifiers[${index}]`;if(!isRecord(modifier))return invalid("invalidEngineering",modifierPath,"objectRequired");if(typeof modifier.Label!=="string")return invalid("invalidEngineering",`${modifierPath}.Label`,"stringRequired");if(!isOptionalFiniteNumber(modifier.Value))return invalid("invalidEngineering",`${modifierPath}.Value`,"finiteNumberRequired");if(!isOptionalFiniteNumber(modifier.OriginalValue))return invalid("invalidEngineering",`${modifierPath}.OriginalValue`,"finiteNumberRequired");if(!isOptionalString(modifier.ValueStr))return invalid("invalidEngineering",`${modifierPath}.ValueStr`,"stringRequired");if(!isOptionalIntegerInRange(modifier.LessIsGood,0,1))return invalid("invalidEngineering",`${modifierPath}.LessIsGood`,"binaryInteger")}}return null}function diagnoseModule(value,path){if(!isRecord(value))return invalid("invalidModule",path,"objectRequired");if(typeof value.Slot!=="string")return invalid("invalidModule",`${path}.Slot`,"stringRequired");if(typeof value.Item!=="string")return invalid("invalidModule",`${path}.Item`,"stringRequired");if(value.On!==void 0&&typeof value.On!=="boolean")return invalid("invalidModule",`${path}.On`,"booleanRequired");if(!isOptionalIntegerInRange(value.Priority,0,4))return invalid("invalidModule",`${path}.Priority`,"priorityRange");if(!isOptionalNumberInRange(value.Health,0,1))return invalid("invalidModule",`${path}.Health`,"unitInterval");if(!isOptionalNumberInRange(value.Value,0))return invalid("invalidModule",`${path}.Value`,"nonNegativeNumberRequired");if(value.Engineering!==void 0)return diagnoseEngineering(value.Engineering,`${path}.Engineering`);return null}function diagnoseHeader(value,path){if(!isRecord(value))return invalid("invalidHeader",path,"objectRequired");if(typeof value.appName!=="string")return invalid("invalidHeader",`${path}.appName`,"stringRequired");if(typeof value.appVersion!=="string"&&!(typeof value.appVersion==="number"&&Number.isFinite(value.appVersion)))return invalid("invalidHeader",`${path}.appVersion`,"versionRequired");if(!isOptionalString(value.appURL))return invalid("invalidHeader",`${path}.appURL`,"stringRequired");if(value.appCustomProperties!==void 0&&!isRecord(value.appCustomProperties))return invalid("invalidHeader",`${path}.appCustomProperties`,"objectRequired");return null}function diagnoseLoadout(value,path){if(!isRecord(value))return invalid("invalidLoadout",path,"objectRequired");if(typeof value.Ship!=="string")return invalid("invalidLoadout",`${path}.Ship`,"stringRequired");if(!Array.isArray(value.Modules))return invalid("invalidLoadout",`${path}.Modules`,"arrayRequired");if(value.event!==void 0&&value.event!=="Loadout")return invalid("invalidLoadout",`${path}.event`,"loadoutEventRequired");for(const field of["ShipName","ShipIdent"]){if(!isOptionalString(value[field]))return invalid("invalidLoadout",`${path}.${field}`,"stringRequired")}for(const field of["HullValue","ModulesValue","UnladenMass","CargoCapacity","MaxJumpRange","Rebuy"]){if(!isOptionalNumberInRange(value[field],0))return invalid("invalidLoadout",`${path}.${field}`,"nonNegativeNumberRequired")}if(value.FuelCapacity!==void 0){if(!isRecord(value.FuelCapacity))return invalid("invalidLoadout",`${path}.FuelCapacity`,"objectRequired");for(const field of["Main","Reserve"]){if(!isOptionalNumberInRange(value.FuelCapacity[field],0)||value.FuelCapacity[field]===void 0)return invalid("invalidLoadout",`${path}.FuelCapacity.${field}`,"nonNegativeNumberRequired")}}for(let index=0;index<value.Modules.length;index+=1){const moduleDiagnostic=diagnoseModule(value.Modules[index],`${path}.Modules[${index}]`);if(moduleDiagnostic!==null)return moduleDiagnostic}return null}function isSlefHeader(value){return diagnoseHeader(value,"header")===null}function isLoadout(value){return diagnoseLoadout(value,"loadout")===null}function duplicateSlot(loadout){const seen=/* @__PURE__ */new Set;for(let moduleIndex=0;moduleIndex<loadout.Modules.length;moduleIndex+=1){const module=loadout.Modules[moduleIndex];const slot=module.Slot.toLowerCase();if(seen.has(slot))return{slot:module.Slot,moduleIndex:moduleIndex};seen.add(slot)}return null}function inspectSlef(input){const root=typeof input==="string"?JSON.parse(input):input;const rawEntries=Array.isArray(root)?root:[root];const entries=[];const diagnostics=[];rawEntries.forEach((raw,index)=>{const entryPath=`entries[${index}]`;let header=null;let loadout=null;if(isLoadout(raw)){header=SYNTHETIC_HEADER;loadout=raw}else if(isRecord(raw)&&isLoadout(raw.data)){if(isSlefHeader(raw.header)){header=raw.header;loadout=raw.data}}if(header===null||loadout===null){const envelope=isRecord(raw)&&("header"in raw||"data"in raw);const detail=envelope?diagnoseHeader(isRecord(raw)?raw.header:null,`${entryPath}.header`)??diagnoseLoadout(isRecord(raw)?raw.data:null,`${entryPath}.data`):diagnoseLoadout(raw,entryPath);diagnostics.push({index:index,code:detail?.code??"invalidLoadout",path:detail?.path??entryPath,constraint:detail?.constraint??"validLoadoutRequired",message:`${detail?.path??entryPath} ${detail?.reason??"is not a valid Loadout event"}`,params:{path:detail?.path??entryPath,constraint:detail?.constraint??"validLoadoutRequired"}});return}const duplicate=duplicateSlot(loadout);if(duplicate!==null){diagnostics.push({index:index,code:"duplicateSlot",path:`${entryPath}${isLoadout(raw)?"":".data"}.Modules[${duplicate.moduleIndex}].Slot`,constraint:"uniqueSlot",message:`Entry ${index} contains duplicate slot "${truncate(duplicate.slot)}"`,params:{index:index,slot:duplicate.slot}});return}entries.push({header:header,data:loadout})});return Object.freeze({entries:Object.freeze(entries),diagnostics:Object.freeze(diagnostics.map(diagnostic=>Object.freeze({...diagnostic,...diagnostic.params?{params:Object.freeze({...diagnostic.params})}:{}})))})}function parseSlef(input){const inspected=inspectSlef(input);if(inspected.diagnostics.length>0){throw new TypeError(`parseSlef: ${inspected.diagnostics[0].message}`)}if(inspected.entries.length===0){throw new TypeError("parseSlef: input holds no entries")}return[...inspected.entries]}function toSlef(data,header){if(!isSlefHeader(header)){throw new TypeError("toSlef: header needs a string `appName` and an `appVersion`")}const events=Array.isArray(data)?data:[data];if(events.length===0){throw new TypeError("toSlef: needs at least one Loadout")}return events.map((event,index)=>{if(!isLoadout(event)){throw new TypeError(`toSlef: entry ${index} is not a valid Loadout (needs a \`Ship\` and \`Modules\`)`)}const duplicate=duplicateSlot(event);if(duplicate!==null){throw new TypeError(`toSlef: entry ${index} contains duplicate slot "${truncate(duplicate.slot)}"`)}return{header:header,data:event}})}function stringifySlef(slef,options={}){return JSON.stringify(slef,null,options.indent??0)}function getLoadoutModifier(module,label){const wanted=normalizeKey(label,"getLoadoutModifier: label");const mod=module.Engineering?.Modifiers?.find(m=>m.Label.toLowerCase()===wanted);return typeof mod?.Value==="number"?mod.Value:null}export{getLoadoutModifier,inspectSlef,parseSlef,stringifySlef,toSlef};//# sourceMappingURL=chunk-7MKYFCFF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ships/slef.ts"],"names":["normalizeKey","truncate","SYNTHETIC_HEADER","Object","freeze","appName","appVersion","isRecord","value","Array","isArray","isOptionalString","isOptionalFiniteNumber","Number","isFinite","isOptionalNumberInRange","minimum","maximum","POSITIVE_INFINITY","isOptionalIntegerInRange","isInteger","CONSTRAINT_MESSAGES","objectRequired","stringRequired","booleanRequired","arrayRequired","finiteNumberRequired","nonNegativeNumberRequired","priorityRange","engineeringLevelRange","unitInterval","binaryInteger","versionRequired","loadoutEventRequired","validLoadoutRequired","invalid","code","path","constraint","reason","diagnoseEngineering","BlueprintName","Level","Quality","ExperimentalEffect","ExperimentalEffect_Localised","Modifiers","index","length","modifier","modifierPath","Label","Value","OriginalValue","ValueStr","LessIsGood","diagnoseModule","Slot","Item","On","Priority","Health","Engineering","diagnoseHeader","appURL","appCustomProperties","diagnoseLoadout","Ship","Modules","event","field","FuelCapacity","moduleDiagnostic","isSlefHeader","isLoadout","duplicateSlot","loadout","seen","Set","moduleIndex","module","slot","toLowerCase","has","add","inspectSlef","input","root","JSON","parse","rawEntries","entries","diagnostics","forEach","raw","entryPath","header","data","envelope","detail","push","message","params","duplicate","map","diagnostic","parseSlef","inspected","TypeError","toSlef","events","stringifySlef","slef","options","stringify","indent","getLoadoutModifier","label","wanted","mod","find","m"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ var phaseTime=(megajoules,rate,draw,capacity,recharge)=>{if(megajoules<=0)return 0;if(rate<=0)return Infinity;if(draw<=0)return megajoules/rate;const drain=rate*draw-recharge;if(drain<=0)return megajoules/rate;const capacitorLifetime=capacity/drain;const beforeEmpty=capacitorLifetime*rate;if(megajoules<=beforeEmpty)return megajoules/rate;if(recharge<=0)return Infinity;return capacitorLifetime+(megajoules-beforeEmpty)/(recharge/draw)};var requireFiniteNonNegative=(name,value)=>{if(!Number.isFinite(value)||value<0){throw new RangeError(`shieldRecovery: ${name} must be a finite non-negative number`)}};function shieldRecovery(input){const pips=input.systemsPips??4;if(!Number.isFinite(pips)||pips<0||pips>4){throw new RangeError("shieldRecovery: systemsPips must be a finite number from 0 to 4")}for(const field of["strength","regenRate","brokenRegenRate","distributorDraw","systemsCapacity","systemsRecharge"]){requireFiniteNonNegative(field,input[field])}const recharge=input.systemsRecharge*Math.pow(pips/4,1.1);const half=input.strength/2;return{regenRate:input.regenRate,brokenRegenRate:input.brokenRegenRate,recoveryTime:16+phaseTime(half,input.brokenRegenRate,input.distributorDraw,input.systemsCapacity,recharge),regenTime:phaseTime(half,input.regenRate,input.distributorDraw,input.systemsCapacity,recharge)}}function cellBankSummary(banks){const metrics=banks.map(bank=>Object.freeze({slot:bank.slot,symbol:bank.symbol,reinforcement:bank.reinforcementRate*bank.duration,cells:bank.cells,spinUp:bank.spinUp,duration:bank.duration,heat:bank.heat}));return Object.freeze({banks:Object.freeze(metrics),totalRestorable:metrics.reduce((sum,bank)=>sum+bank.reinforcement*bank.cells,0),totalCells:metrics.reduce((sum,bank)=>sum+bank.cells,0)})}export{cellBankSummary,shieldRecovery};//# sourceMappingURL=chunk-BUV3MXGK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ships/shield-recovery.ts"],"names":["phaseTime","megajoules","rate","draw","capacity","recharge","Infinity","drain","capacitorLifetime","beforeEmpty","requireFiniteNonNegative","name","value","Number","isFinite","RangeError","shieldRecovery","input","pips","systemsPips","field","systemsRecharge","Math","pow","half","strength","regenRate","brokenRegenRate","recoveryTime","distributorDraw","systemsCapacity","regenTime","cellBankSummary","banks","metrics","map","bank","Object","freeze","slot","symbol","reinforcement","reinforcementRate","duration","cells","spinUp","heat","totalRestorable","reduce","sum","totalCells"],"mappings":"AAsFA,IAAMA,UAAY,CACdC,WACAC,KACAC,KACAC,SACAC,YAEA,GAAIJ,YAAc,EAAG,OAAO,EAC5B,GAAIC,MAAQ,EAAG,OAAOI,SACtB,GAAIH,MAAQ,EAAG,OAAOF,WAAaC,KACnC,MAAMK,MAAQL,KAAOC,KAAOE,SAC5B,GAAIE,OAAS,EAAG,OAAON,WAAaC,KACpC,MAAMM,kBAAoBJ,SAAWG,MACrC,MAAME,YAAcD,kBAAoBN,KACxC,GAAID,YAAcQ,YAAa,OAAOR,WAAaC,KACnD,GAAIG,UAAY,EAAG,OAAOC,SAC1B,OAAOE,mBAAqBP,WAAaQ,cAAgBJ,SAAWF,OAGxE,IAAMO,yBAA2B,CAACC,KAAcC,SAC5C,IAAKC,OAAOC,SAASF,QAAUA,MAAQ,EAAG,CACtC,MAAM,IAAIG,WAAW,mBAAmBJ,4CAC5C,GAoBG,SAASK,eAAeC,OAC3B,MAAMC,KAAOD,MAAME,aAAe,EAClC,IAAKN,OAAOC,SAASI,OAASA,KAAO,GAAKA,KAAO,EAAG,CAChD,MAAM,IAAIH,WAAW,kEACzB,CACA,IAAA,MAAWK,QAAS,CAChB,WACA,YACA,kBACA,kBACA,kBACA,mBACQ,CACRV,yBAAyBU,MAAOH,MAAMG,OAC1C,CACA,MAAMf,SAAWY,MAAMI,gBAAkBC,KAAKC,IAAIL,KAAO,EAAG,KAC5D,MAAMM,KAAOP,MAAMQ,SAAW,EAC9B,MAAO,CACHC,UAAWT,MAAMS,UACjBC,gBAAiBV,MAAMU,gBACvBC,aACI,GACA5B,UACIwB,KACAP,MAAMU,gBACNV,MAAMY,gBACNZ,MAAMa,gBACNzB,UAER0B,UAAW/B,UACPwB,KACAP,MAAMS,UACNT,MAAMY,gBACNZ,MAAMa,gBACNzB,UAGZ,CAiBO,SAAS2B,gBAAgBC,OAC5B,MAAMC,QAAUD,MAAME,IAAKC,MACvBC,OAAOC,OAAO,CACVC,KAAMH,KAAKG,KACXC,OAAQJ,KAAKI,OACbC,cAAeL,KAAKM,kBAAoBN,KAAKO,SAC7CC,MAAOR,KAAKQ,MACZC,OAAQT,KAAKS,OACbF,SAAUP,KAAKO,SACfG,KAAMV,KAAKU,QAGnB,OAAOT,OAAOC,OAAO,CACjBL,MAAOI,OAAOC,OAAOJ,SACrBa,gBAAiBb,QAAQc,OAAO,CAACC,IAAKb,OAASa,IAAMb,KAAKK,cAAgBL,KAAKQ,MAAO,GACtFM,WAAYhB,QAAQc,OAAO,CAACC,IAAKb,OAASa,IAAMb,KAAKQ,MAAO,IAEpE,QAAAZ,gBAAAhB"}