@elite-dangerous-almanac/core 0.2.1 → 0.2.3
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.
- package/PROVENANCE/ships/SOURCES.md +25 -0
- package/README.md +3 -2
- package/THIRD_PARTY_NOTICES.md +24 -2
- package/dist/astro/body-orbit.d.ts +174 -0
- package/dist/astro/body-orbit.js +1 -0
- package/dist/astro/body-orbit.js.map +1 -0
- package/dist/astro/body-physics.d.ts +228 -0
- package/dist/astro/body-physics.js +1 -0
- package/dist/astro/body-physics.js.map +1 -0
- package/dist/astro/body-rings.d.ts +223 -0
- package/dist/astro/body-rings.js +1 -0
- package/dist/astro/body-rings.js.map +1 -0
- package/dist/astro/codex-region-lookup.js +1 -1
- package/dist/astro/codex-region-lookup.js.map +1 -1
- package/dist/astro/index.d.ts +38 -1
- package/dist/astro/index.js +1 -1
- package/dist/astro/nebulae.d.ts +4 -4
- package/dist/astro/procedural-system.js +1 -1
- package/dist/astro/star-physics.d.ts +255 -0
- package/dist/astro/star-physics.js +1 -0
- package/dist/astro/star-physics.js.map +1 -0
- package/dist/body-scan-CEUYjCfH.d.ts +416 -0
- package/dist/{chunk-HUYYGTRM.js → chunk-3LZXB2MC.js} +1 -1
- package/dist/{chunk-AAE4KZ2V.js → chunk-6IWKFKQF.js} +1 -1
- package/dist/{chunk-AAE4KZ2V.js.map → chunk-6IWKFKQF.js.map} +1 -1
- package/dist/{chunk-MRTTQYT5.js → chunk-7EWZGDWY.js} +1 -1
- package/dist/{chunk-TBZJIQ5P.js → chunk-7TVFV3VO.js} +1 -1
- package/dist/chunk-7TVFV3VO.js.map +1 -0
- package/dist/chunk-7ZERP4TB.js +1 -0
- package/dist/{chunk-APEG4ZER.js.map → chunk-7ZERP4TB.js.map} +1 -1
- package/dist/chunk-AGRSIKNV.js +1 -0
- package/dist/chunk-AGRSIKNV.js.map +1 -0
- package/dist/chunk-CHA6LKH4.js +1 -0
- package/dist/chunk-CHA6LKH4.js.map +1 -0
- package/dist/chunk-DABPLBTN.js +1 -0
- package/dist/chunk-DABPLBTN.js.map +1 -0
- package/dist/chunk-DAQ3PJ4R.js +1 -0
- package/dist/chunk-DAQ3PJ4R.js.map +1 -0
- package/dist/chunk-G2VSQFD6.js +1 -0
- package/dist/{chunk-T62GO4AA.js.map → chunk-G2VSQFD6.js.map} +1 -1
- package/dist/chunk-GBTCWNO6.js +1 -0
- package/dist/chunk-GBTCWNO6.js.map +1 -0
- package/dist/chunk-KPJ77VPS.js +1 -0
- package/dist/{chunk-PL75RTA2.js.map → chunk-KPJ77VPS.js.map} +1 -1
- package/dist/chunk-NEXDA73Y.js +1 -0
- package/dist/chunk-NEXDA73Y.js.map +1 -0
- package/dist/chunk-P2MUFVJC.js +1 -0
- package/dist/chunk-P2MUFVJC.js.map +1 -0
- package/dist/chunk-TMXKC6JL.js +1 -0
- package/dist/chunk-TMXKC6JL.js.map +1 -0
- package/dist/chunk-ZZ376WHL.js +1 -0
- package/dist/chunk-ZZ376WHL.js.map +1 -0
- package/dist/commodities/commodities.d.ts +1 -1
- package/dist/equipment/index.js +1 -1
- package/dist/equipment/upgrade-costs.js +1 -1
- package/dist/equipment/upgrade-costs.js.map +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/pre-engineered.js +1 -1
- package/dist/materials/index.js +1 -1
- package/dist/materials/materials.d.ts +1 -1
- package/dist/materials/micro-resources-all.js +1 -1
- package/dist/materials/micro-resources.d.ts +1 -1
- package/dist/materials/micro-resources.js +1 -1
- package/dist/ships/build-metrics.d.ts +21 -232
- package/dist/ships/build-metrics.js +1 -1
- package/dist/ships/distributor.d.ts +1 -1
- package/dist/ships/heat.d.ts +1 -1
- package/dist/ships/index.d.ts +2 -2
- package/dist/ships/index.js +1 -1
- package/dist/ships/loadout-calculations.d.ts +2 -2
- package/dist/ships/mobility-capacitor.d.ts +1 -1
- package/dist/ships/modules-all.js +1 -1
- package/dist/ships/modules.d.ts +1 -1
- package/dist/ships/modules.js +1 -1
- package/dist/ships/pre-engineered-stats.d.ts +16 -9
- package/dist/ships/pre-engineered-stats.js +1 -1
- package/dist/ships/pre-engineered.d.ts +4 -3
- package/dist/ships/pre-engineered.js +1 -1
- package/dist/ships/shield-capacitor.d.ts +1 -1
- package/dist/ships/shields.d.ts +1 -1
- package/dist/ships/ship-loadout.d.ts +80 -16
- package/dist/ships/ship-loadout.js +1 -1
- package/package.json +19 -4
- package/dist/chunk-6S36AVA4.js +0 -1
- package/dist/chunk-6S36AVA4.js.map +0 -1
- package/dist/chunk-APEG4ZER.js +0 -1
- package/dist/chunk-GVNTKCVF.js +0 -1
- package/dist/chunk-GVNTKCVF.js.map +0 -1
- package/dist/chunk-JET673E5.js +0 -1
- package/dist/chunk-JET673E5.js.map +0 -1
- package/dist/chunk-PL75RTA2.js +0 -1
- package/dist/chunk-SG6YLUQ4.js +0 -1
- package/dist/chunk-SG6YLUQ4.js.map +0 -1
- package/dist/chunk-T62GO4AA.js +0 -1
- package/dist/chunk-TBZJIQ5P.js.map +0 -1
- /package/dist/{chunk-HUYYGTRM.js.map → chunk-3LZXB2MC.js.map} +0 -0
- /package/dist/{chunk-MRTTQYT5.js.map → chunk-7EWZGDWY.js.map} +0 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { SystemAddressInput } from './astro/system-address-input.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shape a scanned body arrives in: {@link BodyProperties}, the physical body every
|
|
5
|
+
* calculation reads, and {@link BodyScanEvent}, that body plus the game journal's own
|
|
6
|
+
* `Scan` bookkeeping — with the records both nest.
|
|
7
|
+
*
|
|
8
|
+
* Types only. No catalogue, no parser, no computation, so importing costs nothing at
|
|
9
|
+
* runtime; `./astro` re-exports every symbol, which is why there is no subpath of its own.
|
|
10
|
+
* The orientation a reader needs lives on the two interfaces themselves.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One step up a body's parent chain, as one entry of {@link BodyScanEvent.Parents}.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* **Exactly one key is present**, and its value is that parent's
|
|
20
|
+
* {@link BodyScanEvent.BodyID} — so a moon orbiting a planet reads `{ Planet: 4 }`.
|
|
21
|
+
* The four keys are the four kinds of thing a body can orbit; `Null` is a barycentre,
|
|
22
|
+
* which is a point rather than a body and has no scan of its own.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import type { BodyParent } from '@elite-dangerous-almanac/core/astro';
|
|
27
|
+
*
|
|
28
|
+
* // A moon of the second planet, which orbits the main star.
|
|
29
|
+
* const parents: readonly BodyParent[] = [{ Planet: 4 }, { Star: 0 }];
|
|
30
|
+
*
|
|
31
|
+
* Object.keys(parents[0] ?? {})[0]; // -> 'Planet'
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
interface BodyParent {
|
|
35
|
+
/** `BodyID` of the star this body orbits. */
|
|
36
|
+
readonly Star?: number;
|
|
37
|
+
/** `BodyID` of the planet this body orbits — the body is a moon of it. */
|
|
38
|
+
readonly Planet?: number;
|
|
39
|
+
/** `BodyID` of the ring this body sits in — written for a belt cluster. */
|
|
40
|
+
readonly Ring?: number;
|
|
41
|
+
/** `BodyID` of the barycentre this body orbits. A point, not a scannable body. */
|
|
42
|
+
readonly Null?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* One ring or belt around a body, as one entry of {@link BodyProperties.Rings}.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* The same shape covers a planet's rings and a star's asteroid belts; `Name` tells them
|
|
49
|
+
* apart, ending in `" A Ring"` or `" A Belt"`. Radii are **metres** from the body's
|
|
50
|
+
* centre, and `MassMT` is **megatonnes** — the one mass in this module that is neither
|
|
51
|
+
* solar nor Earth masses.
|
|
52
|
+
*/
|
|
53
|
+
interface BodyRing {
|
|
54
|
+
/** The ring's in-game name, e.g. `"Synuefe EN-H d11-96 A 1 A Ring"`. */
|
|
55
|
+
readonly Name: string;
|
|
56
|
+
/**
|
|
57
|
+
* The ring's material class, as the game's internal token: `"eRingClass_Rocky"`,
|
|
58
|
+
* `"eRingClass_Icy"`, `"eRingClass_MetalRich"` or `"eRingClass_Metalic"`.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* `"eRingClass_Metalic"` is spelt that way by the game. It is Frontier's token, not a
|
|
62
|
+
* typo introduced here, and a consumer matching on the string must spell it likewise.
|
|
63
|
+
*/
|
|
64
|
+
readonly RingClass: string;
|
|
65
|
+
/** The ring's total mass, in megatonnes. */
|
|
66
|
+
readonly MassMT: number;
|
|
67
|
+
/** Inner radius, in metres from the body's centre. */
|
|
68
|
+
readonly InnerRad: number;
|
|
69
|
+
/** Outer radius, in metres from the body's centre. */
|
|
70
|
+
readonly OuterRad: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One gas in a body's atmosphere, as one entry of
|
|
74
|
+
* {@link BodyProperties.AtmosphereComposition}.
|
|
75
|
+
*/
|
|
76
|
+
interface AtmosphereComponent {
|
|
77
|
+
/** The gas, as the game names it, e.g. `"Nitrogen"`, `"SulphurDioxide"`. */
|
|
78
|
+
readonly Name: string;
|
|
79
|
+
/** Share of the atmosphere, as a **percentage** — `99.124542`, not `0.99124542`. */
|
|
80
|
+
readonly Percent: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* One material available at a landable body's surface, as one entry of
|
|
84
|
+
* {@link BodyProperties.Materials}.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* `Name` is the journal's material symbol in lower case, which is what
|
|
88
|
+
* `getMaterialBySymbol` (`../materials`) takes — the same spelling a `MaterialCollected`
|
|
89
|
+
* line uses.
|
|
90
|
+
*/
|
|
91
|
+
interface SurfaceMaterial {
|
|
92
|
+
/** The material's journal symbol, lower-cased, e.g. `"iron"`, `"sulphur"`. */
|
|
93
|
+
readonly Name: string;
|
|
94
|
+
/** The material's display name, when the game localised it into the line. */
|
|
95
|
+
readonly Name_Localised?: string;
|
|
96
|
+
/** Share of the body's surface materials, as a **percentage** out of 100. */
|
|
97
|
+
readonly Percent: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* What a planet or moon is made of, as {@link BodyProperties.Composition}.
|
|
101
|
+
*
|
|
102
|
+
* @remarks
|
|
103
|
+
* Each share is a **fraction of 1**, not a percentage, and the three sum to 1. This is
|
|
104
|
+
* the body's bulk composition; {@link BodyProperties.Materials} is the separate question
|
|
105
|
+
* of what can be collected from its surface.
|
|
106
|
+
*/
|
|
107
|
+
interface BodyComposition {
|
|
108
|
+
/** Ice share of the body, 0–1. */
|
|
109
|
+
readonly Ice: number;
|
|
110
|
+
/** Rock share of the body, 0–1. */
|
|
111
|
+
readonly Rock: number;
|
|
112
|
+
/** Metal share of the body, 0–1. */
|
|
113
|
+
readonly Metal: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The physical body a scan describes — everything a calculation reads, and nothing that
|
|
117
|
+
* names the journal line it arrived on.
|
|
118
|
+
*
|
|
119
|
+
* @remarks
|
|
120
|
+
* {@link BodyScanEvent} extends this with the journal's own bookkeeping — `event`,
|
|
121
|
+
* `timestamp`, `ScanType`, `BodyName`, `BodyID`, `StarSystem`, `SystemAddress`,
|
|
122
|
+
* `DistanceFromArrivalLS`, `Parents` and the discovery flags. None of that is physics, so
|
|
123
|
+
* none of it is required here: every field below is optional, and a body assembled from a
|
|
124
|
+
* database row, an EDSM or Spansh record, or a literal in a test is as good an input as a
|
|
125
|
+
* captured line. A `BodyScanEvent` is a `BodyProperties`, so a parsed journal line passes
|
|
126
|
+
* to every calculation unchanged.
|
|
127
|
+
*
|
|
128
|
+
* Each calculation states which fields it needs and returns `null` when one is missing —
|
|
129
|
+
* see `./body-physics`, `./body-orbit`, `./body-rings` and `./star-physics`.
|
|
130
|
+
*
|
|
131
|
+
* Units are the journal's throughout: metres, seconds, kelvin, pascals, Earth masses for
|
|
132
|
+
* a planet and solar masses for a star. Angles are degrees except
|
|
133
|
+
* {@link BodyProperties.AxialTilt | AxialTilt}, which is radians.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* Only the fields a calculation reads need be present.
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* import type { BodyProperties } from '@elite-dangerous-almanac/core/astro';
|
|
140
|
+
* import { bulkDensity } from '@elite-dangerous-almanac/core/astro/body-physics';
|
|
141
|
+
*
|
|
142
|
+
* const earthLike: BodyProperties = { MassEM: 1, Radius: 6_371_000 };
|
|
143
|
+
*
|
|
144
|
+
* bulkDensity(earthLike); // -> 5513.2…
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
interface BodyProperties {
|
|
148
|
+
/** Semi-major axis of the body's orbit, in **metres**. */
|
|
149
|
+
readonly SemiMajorAxis?: number;
|
|
150
|
+
/** Orbital eccentricity, 0 for a circular orbit and approaching 1 for a long ellipse. */
|
|
151
|
+
readonly Eccentricity?: number;
|
|
152
|
+
/** Orbital inclination, in **degrees**. */
|
|
153
|
+
readonly OrbitalInclination?: number;
|
|
154
|
+
/** Argument of periapsis, in **degrees**. */
|
|
155
|
+
readonly Periapsis?: number;
|
|
156
|
+
/** Time to complete one orbit, in **seconds**. */
|
|
157
|
+
readonly OrbitalPeriod?: number;
|
|
158
|
+
/** Longitude of the ascending node, in **degrees**. */
|
|
159
|
+
readonly AscendingNode?: number;
|
|
160
|
+
/** Mean anomaly at the moment the body was observed, in **degrees**. */
|
|
161
|
+
readonly MeanAnomaly?: number;
|
|
162
|
+
/**
|
|
163
|
+
* Time to complete one rotation, in **seconds**. Negative when the body rotates
|
|
164
|
+
* retrograde.
|
|
165
|
+
*/
|
|
166
|
+
readonly RotationPeriod?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Axial tilt, in **radians** — the one angle here that is not degrees.
|
|
169
|
+
*/
|
|
170
|
+
readonly AxialTilt?: number;
|
|
171
|
+
/** Whether the body keeps one face to its parent. */
|
|
172
|
+
readonly TidalLock?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* The star's spectral class, e.g. `"G"`, `"M"`, `"TTS"` (T Tauri), `"N"` (neutron
|
|
175
|
+
* star), `"H"` (black hole). Present only on a star's line, and the surest way to
|
|
176
|
+
* tell one.
|
|
177
|
+
*/
|
|
178
|
+
readonly StarType?: string;
|
|
179
|
+
/** The spectral subclass, 0–9, hotter at 0. */
|
|
180
|
+
readonly Subclass?: number;
|
|
181
|
+
/** The star's mass, in **solar masses**. */
|
|
182
|
+
readonly StellarMass?: number;
|
|
183
|
+
/** The star's absolute magnitude — smaller is brighter, and it can be negative. */
|
|
184
|
+
readonly AbsoluteMagnitude?: number;
|
|
185
|
+
/** The star's age, in **millions of years**. */
|
|
186
|
+
readonly Age_MY?: number;
|
|
187
|
+
/** The star's luminosity class, e.g. `"V"`, `"Va"`, `"VI"`. */
|
|
188
|
+
readonly Luminosity?: string;
|
|
189
|
+
/**
|
|
190
|
+
* The body's class, e.g. `"High metal content body"`, `"Icy body"`,
|
|
191
|
+
* `"Earthlike body"`, `"Water world"`, `"Sudarsky class I gas giant"`. Present only
|
|
192
|
+
* on a planet's or moon's line.
|
|
193
|
+
*/
|
|
194
|
+
readonly PlanetClass?: string;
|
|
195
|
+
/**
|
|
196
|
+
* Whether the body's terraforming potential has been realised: `""` when there is
|
|
197
|
+
* none, `"Terraformable"` when it is a candidate, `"Terraformed"` when it already is.
|
|
198
|
+
*/
|
|
199
|
+
readonly TerraformState?: string;
|
|
200
|
+
/**
|
|
201
|
+
* The atmosphere as the game describes it in full, e.g.
|
|
202
|
+
* `"thin sulphur dioxide atmosphere"`. `""` when the body has none.
|
|
203
|
+
*/
|
|
204
|
+
readonly Atmosphere?: string;
|
|
205
|
+
/**
|
|
206
|
+
* The atmosphere's dominant gas as a bare token, e.g. `"Nitrogen"`,
|
|
207
|
+
* `"SulphurDioxide"`, `"None"`.
|
|
208
|
+
*
|
|
209
|
+
* @remarks
|
|
210
|
+
* The token drops the density and temperature that {@link BodyProperties.Atmosphere | Atmosphere}
|
|
211
|
+
* spells out, so `"thin nitrogen atmosphere"` and `"hot thick nitrogen atmosphere"`
|
|
212
|
+
* share one `AtmosphereType`. Match on this and read the other for the qualifiers.
|
|
213
|
+
*/
|
|
214
|
+
readonly AtmosphereType?: string;
|
|
215
|
+
/** What the atmosphere is made of, by percentage. */
|
|
216
|
+
readonly AtmosphereComposition?: readonly AtmosphereComponent[];
|
|
217
|
+
/**
|
|
218
|
+
* The body's volcanism as the game describes it, e.g.
|
|
219
|
+
* `"minor silicate vapour geysers volcanism"`. `""` when the body has none.
|
|
220
|
+
*/
|
|
221
|
+
readonly Volcanism?: string;
|
|
222
|
+
/** The body's mass, in **Earth masses**. */
|
|
223
|
+
readonly MassEM?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Surface gravity, in **m/s²** — divide by `9.80665` for the `g` figure the game's
|
|
226
|
+
* own UI shows.
|
|
227
|
+
*/
|
|
228
|
+
readonly SurfaceGravity?: number;
|
|
229
|
+
/**
|
|
230
|
+
* Atmospheric pressure at the surface, in **pascals** — divide by `101325` for
|
|
231
|
+
* atmospheres. `0` on an airless body.
|
|
232
|
+
*/
|
|
233
|
+
readonly SurfacePressure?: number;
|
|
234
|
+
/** Whether a ship can land on the body. */
|
|
235
|
+
readonly Landable?: boolean;
|
|
236
|
+
/** The body's bulk composition, when the scan resolved it. */
|
|
237
|
+
readonly Composition?: BodyComposition;
|
|
238
|
+
/**
|
|
239
|
+
* The materials collectable at the surface, by percentage.
|
|
240
|
+
*
|
|
241
|
+
* @remarks
|
|
242
|
+
* Written for a landable body. Each `Name` is a journal material symbol that
|
|
243
|
+
* `getMaterialBySymbol` (`../materials`) resolves.
|
|
244
|
+
*/
|
|
245
|
+
readonly Materials?: readonly SurfaceMaterial[];
|
|
246
|
+
/**
|
|
247
|
+
* The body's radius, in **metres** — for a star as much as for a planet.
|
|
248
|
+
*/
|
|
249
|
+
readonly Radius?: number;
|
|
250
|
+
/** Surface — or, for a star, photospheric — temperature, in **kelvin**. */
|
|
251
|
+
readonly SurfaceTemperature?: number;
|
|
252
|
+
/** The body's rings and belts, absent when it has none. */
|
|
253
|
+
readonly Rings?: readonly BodyRing[];
|
|
254
|
+
/**
|
|
255
|
+
* How rich the body's rings are, e.g. `"PristineResources"`, `"MajorResources"`,
|
|
256
|
+
* `"CommonResources"`, `"LowResources"`, `"DepletedResources"` — the game's reserve
|
|
257
|
+
* levels, each suffixed `Resources`.
|
|
258
|
+
*
|
|
259
|
+
* @remarks
|
|
260
|
+
* Written only when the body has rings, and it describes those rings rather than the
|
|
261
|
+
* body itself.
|
|
262
|
+
*/
|
|
263
|
+
readonly ReserveLevel?: string;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* A journal `Scan` event — one scanned star, planet, moon or belt cluster, and the
|
|
267
|
+
* library's import shape for body data.
|
|
268
|
+
*
|
|
269
|
+
* @remarks
|
|
270
|
+
* Elite Dangerous writes one `Scan` line per body whenever the discovery scanner, the FSS
|
|
271
|
+
* or a nav beacon resolves it, and that line is the community's lingua franca for bodies:
|
|
272
|
+
* EDDN relays it, EDSM and Spansh store it, every journal-reading app already holds it.
|
|
273
|
+
* So it is what this library takes in, field for field — the journal's own names,
|
|
274
|
+
* capitalisation and units — and a parsed line is already this type:
|
|
275
|
+
*
|
|
276
|
+
* ```ts
|
|
277
|
+
* const scan = JSON.parse(line) as BodyScanEvent;
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* **Units are the journal's, and they are not the ones the game's UI shows.** Radii and
|
|
281
|
+
* orbital distances are metres, not light-seconds; pressure is pascals, not atmospheres;
|
|
282
|
+
* surface gravity is m/s², not g; `AxialTilt` is radians while every other angle is
|
|
283
|
+
* degrees. Every field below says which, because getting this wrong is silent — a
|
|
284
|
+
* plausible number in the wrong scale.
|
|
285
|
+
*
|
|
286
|
+
* **Almost every field is optional, because almost every field is conditional.** A star's
|
|
287
|
+
* line carries no {@link BodyProperties.PlanetClass | PlanetClass}, a planet's no
|
|
288
|
+
* {@link BodyProperties.StarType | StarType}, a belt cluster's little beyond its identity, and a body
|
|
289
|
+
* with no rings no {@link BodyProperties.ReserveLevel | ReserveLevel}. Only `ScanType`, `BodyName`,
|
|
290
|
+
* `BodyID`, `StarSystem`, `SystemAddress`, `DistanceFromArrivalLS`, `WasDiscovered` and
|
|
291
|
+
* `WasMapped` are written for every body. Treat a missing field as "not written for this
|
|
292
|
+
* body, or not resolved by this scan" — never as a zero.
|
|
293
|
+
*
|
|
294
|
+
* **A body has no id64 of its own.** {@link BodyScanEvent.SystemAddress | SystemAddress} identifies the
|
|
295
|
+
* system and {@link BodyScanEvent.BodyID | BodyID} the body within it; the pair is the identity to
|
|
296
|
+
* store and join on.
|
|
297
|
+
*
|
|
298
|
+
* `event` and `timestamp` name the journal line rather than the body, and are optional so
|
|
299
|
+
* that a record reassembled from a database or an EDDN message is still this type.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* A parsed line is already the interface, and its address goes straight into the rest of
|
|
303
|
+
* the astro API.
|
|
304
|
+
*
|
|
305
|
+
* ```ts
|
|
306
|
+
* import type { BodyScanEvent } from '@elite-dangerous-almanac/core/astro';
|
|
307
|
+
* import { ProceduralSystem } from '@elite-dangerous-almanac/core/astro/procedural-system';
|
|
308
|
+
*
|
|
309
|
+
* const scan: BodyScanEvent = {
|
|
310
|
+
* event: 'Scan',
|
|
311
|
+
* ScanType: 'Detailed',
|
|
312
|
+
* BodyName: 'Synuefe EN-H d11-96 A 1',
|
|
313
|
+
* BodyID: 3,
|
|
314
|
+
* StarSystem: 'Synuefe EN-H d11-96',
|
|
315
|
+
* SystemAddress: 3309179996515,
|
|
316
|
+
* DistanceFromArrivalLS: 543.2,
|
|
317
|
+
* WasDiscovered: false,
|
|
318
|
+
* WasMapped: false,
|
|
319
|
+
* };
|
|
320
|
+
*
|
|
321
|
+
* ProceduralSystem.fromSystemAddress(scan.SystemAddress).massCode; // -> 'd'
|
|
322
|
+
* ```
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* Which kind of body a line describes is told by which fields it carries: `StarType` for
|
|
326
|
+
* a star, `PlanetClass` for a planet or moon, neither for a belt cluster.
|
|
327
|
+
*
|
|
328
|
+
* ```ts
|
|
329
|
+
* import type { BodyScanEvent } from '@elite-dangerous-almanac/core/astro';
|
|
330
|
+
*
|
|
331
|
+
* declare const scan: BodyScanEvent;
|
|
332
|
+
*
|
|
333
|
+
* const kind =
|
|
334
|
+
* scan.StarType !== undefined
|
|
335
|
+
* ? 'star'
|
|
336
|
+
* : scan.PlanetClass !== undefined
|
|
337
|
+
* ? 'planet'
|
|
338
|
+
* : 'belt cluster or barycentre';
|
|
339
|
+
* ```
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* A landable body's {@link BodyProperties.Materials | Materials} name journal material symbols, which
|
|
343
|
+
* the materials catalogue reads directly.
|
|
344
|
+
*
|
|
345
|
+
* ```ts
|
|
346
|
+
* import { getMaterialBySymbol } from '@elite-dangerous-almanac/core/materials';
|
|
347
|
+
*
|
|
348
|
+
* getMaterialBySymbol('sulphur')?.name; // -> 'Sulphur'
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
interface BodyScanEvent extends BodyProperties {
|
|
352
|
+
/** `"Scan"` when a journal line supplied it. */
|
|
353
|
+
readonly event?: string;
|
|
354
|
+
/** When the scan happened, ISO 8601 in UTC, e.g. `"2026-08-26T21:04:11Z"`. */
|
|
355
|
+
readonly timestamp?: string;
|
|
356
|
+
/**
|
|
357
|
+
* How the body was scanned: `"AutoScan"` (passive, on approach or from the discovery
|
|
358
|
+
* scanner's honk), `"Basic"`, `"Detailed"` (resolved in the FSS or by flying to the
|
|
359
|
+
* body) or `"NavBeaconDetail"` (read off a populated system's nav beacon).
|
|
360
|
+
*
|
|
361
|
+
* @remarks
|
|
362
|
+
* This is the field that decides how much of the rest is present. A `"Detailed"` or
|
|
363
|
+
* `"NavBeaconDetail"` line is the full record; an `"AutoScan"` of a distant body may
|
|
364
|
+
* carry little more than its identity and orbit.
|
|
365
|
+
*/
|
|
366
|
+
readonly ScanType: string;
|
|
367
|
+
/** The body's in-game name, e.g. `"Synuefe EN-H d11-96 A 1"`. */
|
|
368
|
+
readonly BodyName: string;
|
|
369
|
+
/**
|
|
370
|
+
* The body's id within its system, unique and stable there, with the system's first
|
|
371
|
+
* star at 0.
|
|
372
|
+
*
|
|
373
|
+
* @remarks
|
|
374
|
+
* A body has no galaxy-wide id of its own: `SystemAddress` + `BodyID` is the identity
|
|
375
|
+
* to store and join on. It is also what {@link BodyParent} entries point at.
|
|
376
|
+
*/
|
|
377
|
+
readonly BodyID: number;
|
|
378
|
+
/** The system's name, e.g. `"Synuefe EN-H d11-96"`. */
|
|
379
|
+
readonly StarSystem: string;
|
|
380
|
+
/**
|
|
381
|
+
* The system's `id64` address.
|
|
382
|
+
*
|
|
383
|
+
* @remarks
|
|
384
|
+
* Typed as {@link SystemAddressInput} rather than `number`, so the value survives
|
|
385
|
+
* however the line was parsed: `JSON.parse` yields a `number`, a lossless parser a
|
|
386
|
+
* `bigint`, a database column a decimal `string`. All three pass straight into
|
|
387
|
+
* `decodeSystemAddress`, `ProceduralSystem.fromSystemAddress` and the permit-lock
|
|
388
|
+
* lookups. Addresses reach bit 55, so a `number` beyond `2^53 - 1` has already been
|
|
389
|
+
* rounded — those entry points reject one rather than answer for the wrong system.
|
|
390
|
+
*/
|
|
391
|
+
readonly SystemAddress: SystemAddressInput;
|
|
392
|
+
/**
|
|
393
|
+
* Distance from the system's arrival point, in **light-seconds**. The main star reads
|
|
394
|
+
* `0`.
|
|
395
|
+
*/
|
|
396
|
+
readonly DistanceFromArrivalLS: number;
|
|
397
|
+
/**
|
|
398
|
+
* The body's parent chain, nearest first and ending at the system's root — so a moon
|
|
399
|
+
* reads `[{ Planet: … }, { Star: 0 }]`.
|
|
400
|
+
*
|
|
401
|
+
* @remarks
|
|
402
|
+
* Absent for a body that orbits nothing, i.e. a single-star system's main star.
|
|
403
|
+
*/
|
|
404
|
+
readonly Parents?: readonly BodyParent[];
|
|
405
|
+
/** Whether someone had already discovered the body before this scan. */
|
|
406
|
+
readonly WasDiscovered: boolean;
|
|
407
|
+
/** Whether someone had already surface-mapped the body before this scan. */
|
|
408
|
+
readonly WasMapped: boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Whether someone had already set foot on the body. Written since Odyssey, and only
|
|
411
|
+
* for a body that can be walked on.
|
|
412
|
+
*/
|
|
413
|
+
readonly WasFootfalled?: boolean;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export type { AtmosphereComponent as A, BodyComposition as B, SurfaceMaterial as S, BodyParent as a, BodyProperties as b, BodyRing as c, BodyScanEvent as d };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ALL_MODULES}from"./chunk-
|
|
1
|
+
import{ALL_MODULES}from"./chunk-7ZERP4TB.js";import{findInKeyIndex,findByKey,filterByKey,createKeyIndex}from"./chunk-BKWLI44T.js";var MODULES_BY_SYMBOL=/* @__PURE__ */createKeyIndex(ALL_MODULES,"symbol");function builtInModuleBySymbol(symbol,label){return findInKeyIndex(MODULES_BY_SYMBOL,symbol,label)}function getModuleBySymbol(symbol,modules=ALL_MODULES){return modules===ALL_MODULES?builtInModuleBySymbol(symbol,"getModuleBySymbol: symbol"):findByKey(modules,"symbol",symbol,"getModuleBySymbol: symbol")}function getModulesByName(name,modules=ALL_MODULES){return filterByKey(modules,"name",name,"getModulesByName: name")}function getBulkheadsForShip(ship,modules=ALL_MODULES){return filterByKey(modules,"ship",ship,"getBulkheadsForShip: ship")}export{builtInModuleBySymbol,getBulkheadsForShip,getModuleBySymbol,getModulesByName};//# sourceMappingURL=chunk-3LZXB2MC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getLoadoutModifier}from"./chunk-T4MKUKYS.js";import{sourcePurchaseFromLoadout}from"./chunk-NIURLW2F.js";import{identifyPreEngineeredVariant,unrollableFixedArticle,cloneLoadoutModule,isNonOutfittingSlot,isBuiltInHullModule,matchingKeyIn,ownKeyIn,getPreEngineeredStats,getPreEngineeredModifiers,cloneModuleStats,isFinalGuardianWeaponEngineering,scaleDamageComponents}from"./chunk-JET673E5.js";import{weaponMetrics,combinedRateOfFire}from"./chunk-37JIMWLK.js";import{getPreEngineeredVariants}from"./chunk-PL75RTA2.js";import{shieldMetrics}from"./chunk-5VWACBTN.js";import{getShipBySymbol,getShipByName,getShipSlots}from"./chunk-6UNAD74Z.js";import{builtInModuleBySymbol,getBulkheadsForShip}from"./chunk-HUYYGTRM.js";import{CORE_MODULES}from"./chunk-FA5JVXVG.js";import{getDefaultLoadout}from"./chunk-GRIK3EDH.js";import{getExperimentalEffect}from"./chunk-77I24V6B.js";import{ammunitionCapacity}from"./chunk-AA3K5XUE.js";import{fieldForLabel,scaleForLabel,labelsForField,capabilityValueForLabel,damageTypeForLabel}from"./chunk-CJQ37JII.js";import{parseSlotName,SLOT_RESTRICTION_LABELS,enumerateSlots}from"./chunk-RAO35XDF.js";import{normalizeKey}from"./chunk-BKWLI44T.js";import{truncate,requireString,describeValue,requireStringIfPresent}from"./chunk-PKWVG2JY.js";import{deepFreeze}from"./chunk-DURKO7JU.js";var MILITARY_PREFIXES=["int_hullreinforcement","int_metaalloyhullreinforcement","int_modulereinforcement","int_shieldcellbank","int_guardianhullreinforcement","int_guardianmodulereinforcement","int_guardianshieldreinforcement"];var MINING_PREFIXES=["hpt_mininglaser","hpt_mining_abrblstr","hpt_mining_seismchrgwarhd","hpt_mining_subsurfdispmisle","hpt_human_extraction","hpt_miningtoolv2"];var CARGO_PREFIXES=["int_cargorack","int_largecargorack","int_corrosionproofcargorack","int_fueltank"];var RESTRICTED_SLOT_PREFIXES={mining:MINING_PREFIXES,military:MILITARY_PREFIXES,cargo:CARGO_PREFIXES,limpetController:["int_dronecontrol","int_multidronecontrol"],vesselHangar:["int_fighterbay"],passenger:["int_passengercabin","int_mkii_passengercabin"],planetaryApproachSuite:["int_planetapproachsuite"]};var problem=(constraint,message,params)=>({constraint:constraint,message:message,...params===void 0?{}:{params:params}});function restrictionProblem(slot,symbol){const restriction=slot.restriction;if(!restriction)return null;if(RESTRICTED_SLOT_PREFIXES[restriction].some(prefix=>symbol.startsWith(prefix))){return null}return problem("restrictedMount",`slot only takes ${SLOT_RESTRICTION_LABELS[restriction]}`,{restriction:restriction})}function moduleSlotProblem(slot,module){const required=module.restrictedToSlot;if(!required||slot.restriction===required)return null;return problem("restrictedMount",`module only fits a mount that takes ${SLOT_RESTRICTION_LABELS[required]}`,{restriction:required})}function moduleFitProblem(shipSymbol,slot,module){if(slot.kind==="cargoHatch"){return problem("immutableSlot","the cargoHatch slot cannot be changed")}if(slot.kind==="armour"){const hull=getShipBySymbol(shipSymbol);if(module.slot!=="armour"||module.ship===void 0){return problem("armourRequired","not a ship armour module")}if(!hull||module.ship.toLowerCase()!==hull.name.toLowerCase()){const armourHull=getShipByName(module.ship);return problem("wrongHullArmour",`armour belongs to ${truncate(module.ship)}, not ${truncate(hull?.name??shipSymbol)}`,{armourShipName:module.ship,...armourHull===null?{}:{armourShipSymbol:armourHull.symbol},shipSymbol:shipSymbol,...hull===null?{}:{shipName:hull.name}})}return null}const restricted=module.restrictedToShips;if(restricted&&!restricted.some(symbol2=>symbol2.toLowerCase()===shipSymbol.toLowerCase())){const allowedShipNames=restricted.map(symbol2=>{const hull=getShipBySymbol(symbol2);return hull?.name??symbol2});const labels=restricted.map((symbol2,index)=>{const name=allowedShipNames[index];return name===symbol2?symbol2:`${name} (${symbol2})`});return problem("restrictedHull",`module is restricted to ${truncate(labels.join(", "))}`,{allowedShipNames:allowedShipNames,allowedShipSymbols:[...restricted],shipSymbol:shipSymbol})}const wrongMount=moduleSlotProblem(slot,module);if(wrongMount)return wrongMount;const symbol=module.symbol.toLowerCase();const moduleSlot=module.slot;switch(slot.kind){case"core":if(moduleSlot!==slot.core){return problem("wrongCoreType",`not a ${slot.core} module`,{requiredCore:slot.core,moduleSlot:moduleSlot??"none"})}break;case"hardpoint":{if(module.category!=="hardpoint"){return problem("hardpointRequired","not a hardpoint weapon")}const restricted2=restrictionProblem(slot,symbol);if(restricted2)return restricted2;break}case"utility":if(module.category!=="utility"){return problem("utilityRequired","not a utility module")}return null;case"optional":{const isFuelTank=moduleSlot==="fuelTank";if(module.category!=="internal"&&!isFuelTank){return problem("optionalInternalRequired","not an optional-internal module")}if(moduleSlot&&!isFuelTank){return problem("coreModuleInOptionalSlot","a core module only fits its core slot")}const restricted2=restrictionProblem(slot,symbol);if(restricted2)return restricted2;break}}return module.class>slot.size?problem("oversized",`module size ${truncate(module.class)} exceeds slot size ${slot.size}`,{moduleClass:module.class,slotSize:slot.size}):null}function moduleFitError(shipSymbol,slot,module){return moduleFitProblem(shipSymbol,slot,module)?.message??null}function preEngineeredVariantFor(module){return identifyPreEngineeredVariant(module)??(module.Engineering?unrollableFixedArticle(module.Item,module.Engineering):null)}function captureLoadoutEvent(event){const modules=event.Modules;const shipName=event.ShipName;const shipIdent=event.ShipIdent;const hullValue=event.HullValue;const modulesValue=event.ModulesValue;const rebuy=event.Rebuy;const unladenMass=event.UnladenMass;const cargoCapacity=event.CargoCapacity;const fuelCapacity=event.FuelCapacity;return{Ship:event.Ship,Modules:Array.isArray(modules)?captureEach(modules,captureModule):modules,...shipName===void 0?{}:{ShipName:shipName},...shipIdent===void 0?{}:{ShipIdent:shipIdent},...hullValue===void 0?{}:{HullValue:hullValue},...modulesValue===void 0?{}:{ModulesValue:modulesValue},...rebuy===void 0?{}:{Rebuy:rebuy},...unladenMass===void 0?{}:{UnladenMass:unladenMass},...cargoCapacity===void 0?{}:{CargoCapacity:cargoCapacity},...fuelCapacity===void 0?{}:{FuelCapacity:{...fuelCapacity}}}}function captureEach(values,capture){const captured=[];const{length:length}=values;for(let index=0;index<length;index++)captured.push(capture(values[index]));return captured}function captureModule(module){if(module===null||typeof module!=="object")return module;const on=module.On;const priority=module.Priority;const health=module.Health;const value=module.Value;const engineering=module.Engineering;return{Slot:module.Slot,Item:module.Item,...on===void 0?{}:{On:on},...priority===void 0?{}:{Priority:priority},...health===void 0?{}:{Health:health},...value===void 0?{}:{Value:value},...engineering===void 0?{}:{Engineering:captureEngineering(engineering)}}}function captureEngineering(engineering){if(engineering===null||typeof engineering!=="object")return engineering;const experimental=engineering.ExperimentalEffect;const experimentalLocalised=engineering.ExperimentalEffect_Localised;const modifiers=engineering.Modifiers;return{BlueprintName:engineering.BlueprintName,Level:engineering.Level,Quality:engineering.Quality,...experimental===void 0?{}:{ExperimentalEffect:experimental},...experimentalLocalised===void 0?{}:{ExperimentalEffect_Localised:experimentalLocalised},...modifiers===void 0?{}:{Modifiers:Array.isArray(modifiers)?captureEach(modifiers,modifier=>modifier!==null&&typeof modifier==="object"?{...modifier}:modifier):modifiers}}}function hullLayout(shipSymbol){const slots=getShipSlots(shipSymbol);return slots?enumerateSlots(slots):[]}function fixedMountRejects(shipSymbol,layout,slot,stats){const mount=layout.find(candidate=>candidate.key.toLowerCase()===slot.toLowerCase());return mount!==void 0&&moduleFitProblem(shipSymbol,mount,stats)!==null}function normalizeLoadoutEvent(rawEvent){const event=captureLoadoutEvent(rawEvent);requireString(event.Ship,"ShipLoadout.fromLoadout: event.Ship");if(!Array.isArray(event.Modules)){throw new TypeError(`ShipLoadout.fromLoadout: event.Modules must be an array, received ${describeValue(event.Modules)}`)}const modules=/* @__PURE__ */new Map;const slots=/* @__PURE__ */new Set;for(const module of event.Modules){if(module===null||typeof module!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: event.Modules[] must hold module objects, received ${describeValue(module)}`)}requireString(module.Slot,"ShipLoadout.fromLoadout: module.Slot");requireString(module.Item,"ShipLoadout.fromLoadout: module.Item");if(module.Engineering!==void 0){if(module.Engineering===null||typeof module.Engineering!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering must be an object, received ${describeValue(module.Engineering)}`)}requireStringIfPresent(module.Engineering.BlueprintName,"ShipLoadout.fromLoadout: module.Engineering.BlueprintName");requireStringIfPresent(module.Engineering.ExperimentalEffect,"ShipLoadout.fromLoadout: module.Engineering.ExperimentalEffect");if(module.Engineering.Modifiers!==void 0&&!Array.isArray(module.Engineering.Modifiers)){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering.Modifiers must be an array, received ${describeValue(module.Engineering.Modifiers)}`)}for(const[index,modifier]of(module.Engineering.Modifiers??[]).entries()){if(modifier===null||typeof modifier!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering.Modifiers[${index}] must be an object, received ${describeValue(modifier)}`)}requireString(modifier.Label,`ShipLoadout.fromLoadout: module.Engineering.Modifiers[${index}].Label`)}}const normalizedSlot=module.Slot.toLowerCase();if(slots.has(normalizedSlot)){throw new TypeError(`ShipLoadout.fromLoadout: duplicate slot "${truncate(module.Slot)}"`)}slots.add(normalizedSlot);modules.set(module.Slot,cloneLoadoutModule(module))}const top={};if(event.ShipName!==void 0)top.ShipName=event.ShipName;if(event.ShipIdent!==void 0)top.ShipIdent=event.ShipIdent;if(event.HullValue!==void 0)top.HullValue=event.HullValue;if(event.ModulesValue!==void 0)top.ModulesValue=event.ModulesValue;if(event.Rebuy!==void 0)top.Rebuy=event.Rebuy;if(event.UnladenMass!==void 0)top.UnladenMass=event.UnladenMass;if(event.CargoCapacity!==void 0)top.CargoCapacity=event.CargoCapacity;if(event.FuelCapacity!==void 0)top.FuelCapacity={...event.FuelCapacity};let invalidatesAggregates=false;const outcomes=[];const defaults=getDefaultLoadout(event.Ship)?.modules??[];let layout;for(const[slot,module]of modules){if(isNonOutfittingSlot(slot)||isBuiltInHullModule(module))continue;const stats=builtInModuleBySymbol(module.Item,"ShipLoadout.fromLoadout: module.Item");const slotKind=parseSlotName(slot)?.kind;const fallback=slotKind==="core"||slotKind==="armour"||slotKind==="cargoHatch"?defaults.find(candidate=>candidate.slot.toLowerCase()===slot.toLowerCase()):void 0;const rejected=fallback!==void 0&&(stats===null||slotKind==="cargoHatch"||fixedMountRejects(event.Ship,layout??=hullLayout(event.Ship),slot,stats));if(stats!==null&&!rejected)continue;if(fallback){modules.set(slot,{Slot:slot,Item:fallback.symbol,...module.On===void 0?{}:{On:module.On},...module.Priority===void 0?{}:{Priority:module.Priority},...module.Health===void 0?{}:{Health:module.Health}});outcomes.push({action:"defaulted",slot:slot,sourceSymbol:module.Item,replacementSymbol:fallback.symbol})}else{modules.delete(slot);outcomes.push({action:"emptied",slot:slot,sourceSymbol:module.Item})}invalidatesAggregates=true}for(const fallback of defaults){const slotKind=parseSlotName(fallback.slot)?.kind;if(slotKind!=="core"&&slotKind!=="armour"&&slotKind!=="cargoHatch"||matchingKeyIn(modules,fallback.slot)!==null){continue}const slot=ownKeyIn(modules,fallback.slot);modules.set(slot,{Slot:slot,Item:fallback.symbol});outcomes.push({action:"defaulted",slot:slot,sourceSymbol:null,replacementSymbol:fallback.symbol});if(slotKind==="core")invalidatesAggregates=true}if(invalidatesAggregates){delete top.ModulesValue;delete top.Rebuy;delete top.UnladenMass;delete top.CargoCapacity;delete top.FuelCapacity}const moduleStats=/* @__PURE__ */new Map;const primitiveModifiers=/* @__PURE__ */new Map;for(const module of modules.values()){const engineering=module.Engineering;const variant=preEngineeredVariantFor(module);const catalogueVariantStats=variant?getPreEngineeredStats(variant):null;let variantStats=catalogueVariantStats;const retainsBakedExperimental=variant?.experimentalEffectSymbol!==void 0&&typeof engineering?.ExperimentalEffect==="string"&&variant.experimentalEffectSymbol.trim().toLowerCase()===engineering.ExperimentalEffect.trim().toLowerCase();if(variant&&catalogueVariantStats&&!catalogueVariantStats.engineeringLocked&&!retainsBakedExperimental){const{experimentalEffectSymbol:originalExperimental,...withoutExperimental}=variant;const currentExperimental=engineering?.ExperimentalEffect;const currentVariant=typeof currentExperimental==="string"?{...withoutExperimental,experimentalEffectSymbol:currentExperimental}:withoutExperimental;variantStats=getPreEngineeredStats(withoutExperimental);primitiveModifiers.set(module.Slot,[...engineering?.Modifiers??[],...getPreEngineeredModifiers(currentVariant)])}if(variantStats)moduleStats.set(module.Slot,cloneModuleStats(variantStats));const guardianFinal=typeof engineering?.BlueprintName==="string"&&isFinalGuardianWeaponEngineering(module.Item,engineering.BlueprintName);if(variantStats?.engineeringLocked||typeof engineering?.BlueprintName!=="string"||!guardianFinal){continue}const normalizedBlueprint=normalizeKey(engineering.BlueprintName,"ShipLoadout.fromLoadout: module.Engineering.BlueprintName");const normalizedExperimental=normalizeKey(engineering.ExperimentalEffect,"ShipLoadout.fromLoadout: module.Engineering.ExperimentalEffect");const variants=getPreEngineeredVariants(module.Item);const exact=variants.find(candidate=>candidate.engineeringLocked===true&&candidate.blueprintSymbol.toLowerCase()===normalizedBlueprint&&candidate.grade===engineering.Level&&candidate.experimentalEffectSymbol?.toLowerCase()===normalizedExperimental);const guardianBase=variants.find(candidate=>candidate.engineeringLocked===true&&candidate.blueprintSymbol.toLowerCase()===normalizedBlueprint&&candidate.grade===engineering.Level&&candidate.experimentalEffectSymbol===void 0);const stats=exact?getPreEngineeredStats(exact):guardianBase&&engineering.ExperimentalEffect!==void 0?getPreEngineeredStats({...guardianBase,experimentalEffectSymbol:engineering.ExperimentalEffect}):builtInModuleBySymbol(module.Item,"ShipLoadout.fromLoadout: module.Item");if(stats){moduleStats.set(module.Slot,cloneModuleStats({...stats,engineeringLocked:true}))}}return{shipSymbol:event.Ship,modules:modules,moduleStats:moduleStats,primitiveModifiers:primitiveModifiers,top:top,sourcePurchase:sourcePurchaseFromLoadout(event),outcomes:deepFreeze(outcomes)}}var NO_ISSUES=Object.freeze([]);function completeResult(value){return Object.freeze({value:value,complete:true,issues:NO_ISSUES})}function incompleteResult(issues){return Object.freeze({value:null,complete:false,issues:Object.freeze(issues.map(issue=>Object.freeze({...issue,...issue.params?{params:Object.freeze({...issue.params})}:{}})))})}var PREFIX={powerPlant:["int_powerplant","int_guardianpowerplant"],thrusters:["int_engine","int_mkiiagileboost_engine"],frameShiftDrive:["int_hyperdrive"],powerDistributor:["int_powerdistributor","int_guardianpowerdistributor"],shieldGenerator:["int_shieldgenerator"],shieldCellBank:["int_shieldcellbank"],shieldBooster:["hpt_shieldbooster"],shieldReinforcement:["int_guardianshieldreinforcement"],hullReinforcement:["int_hullreinforcement","int_guardianhullreinforcement","int_metaalloyhullreinforcement"],moduleReinforcement:["int_modulereinforcement","int_guardianmodulereinforcement"]};var startsWithAny=(symbol,prefixes)=>prefixes.some(prefix=>symbol.toLowerCase().startsWith(prefix));function isPowerDistributor(module,stats){return stats?.slot?stats.slot==="powerDistributor":startsWithAny(module.Item,PREFIX.powerDistributor)}function isPowerPlant(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="powerPlant")return true;return stats?.slot?stats.slot==="powerPlant":startsWithAny(module.Item,PREFIX.powerPlant)}function isThruster(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="thrusters")return true;return stats?.slot?stats.slot==="thrusters":startsWithAny(module.Item,PREFIX.thrusters)}function isFrameShiftDrive(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="frameShiftDrive")return true;return stats?.slot?stats.slot==="frameShiftDrive":startsWithAny(module.Item,PREFIX.frameShiftDrive)}function isEnabled(module){return module.On!==false}function priorityOf(module){return(module.Priority??0)+1}function effectiveStat(module,field,stats){const stated=statedModifier(module,field);if(stated!==void 0)return stated;const base=stats?.[field];return typeof base==="number"?base:void 0}function statedModifier(module,field){for(const label of labelsForField(field)){if(capabilityValueForLabel(label)!==null)continue;const modified=getLoadoutModifier(module,label);if(modified!==null)return modified/scaleForLabel(label)}return void 0}function effectiveCapability(module,field,stats){for(const label of labelsForField(field)){if(capabilityValueForLabel(label)===null)continue;if(module.Engineering?.Modifiers?.some(modifier=>modifier.Label.toLowerCase()===label.toLowerCase())){return true}}const base=stats[field];return typeof base==="boolean"?base:void 0}function effectiveDamageDistribution(module,stats){const fromEffect=module.Engineering?.ExperimentalEffect?getExperimentalEffect(module.Engineering.ExperimentalEffect)?.damageDistribution:void 0;const distribution={...fromEffect??stats.damageDistribution};let resolved=fromEffect!==void 0||stats.damageDistribution!==void 0;const seen=/* @__PURE__ */new Set;for(const modifier of module.Engineering?.Modifiers??[]){const type=damageTypeForLabel(modifier.Label);if(type===null||seen.has(type))continue;seen.add(type);if(modifier.Value===void 0)continue;distribution[type]=modifier.Value/scaleForLabel(modifier.Label);resolved=true}return resolved?distribution:void 0}function convertsDamage(module){if(module.Engineering?.ExperimentalEffect&&getExperimentalEffect(module.Engineering.ExperimentalEffect)?.damageDistribution){return true}return(module.Engineering?.Modifiers??[]).some(modifier=>damageTypeForLabel(modifier.Label)!==null)}function modifierRatio(module,stats,field){const base=stats?.[field];if(typeof base!=="number"||base===0)return 1;const effective=effectiveStat(module,field,stats);return effective===void 0?1:effective/base}function relatedStat(module,stats,field,ratio){const stated=statedModifier(module,field);if(stated!==void 0)return stated;const base=stats?.[field];return typeof base==="number"?base*ratio:void 0}function effectiveModule(module,stats){if(!stats||!module.Engineering)return stats;const merged={...stats};const fields=new Set(Object.keys(stats));for(const modifier of module.Engineering.Modifiers??[]){const field=fieldForLabel(modifier.Label,stats);if(field)fields.add(field)}for(const key of fields){const capability=effectiveCapability(module,key,stats);if(capability!==void 0){merged[key]=capability;continue}const value=effectiveStat(module,key,stats);if(value!==void 0)merged[key]=value}if(stats.engineeringGroup==="thrusters"){const massRatio=modifierRatio(module,stats,"optMass");const minMass=relatedStat(module,stats,"minMass",massRatio);const maxMass=relatedStat(module,stats,"maxMass",massRatio);if(minMass!==void 0)merged.minMass=minMass;if(maxMass!==void 0)merged.maxMass=maxMass}if(stats.engineeringGroup==="thrusters"||stats.engineeringGroup==="shieldGenerators"){const performanceRatio=modifierRatio(module,stats,"optMultiplier");const minMultiplier=relatedStat(module,stats,"minMultiplier",performanceRatio);const maxMultiplier=relatedStat(module,stats,"maxMultiplier",performanceRatio);if(minMultiplier!==void 0)merged.minMultiplier=minMultiplier;if(maxMultiplier!==void 0)merged.maxMultiplier=maxMultiplier;if(stats.engineeringGroup==="thrusters"){for(const field of["minSpeedMultiplier","optSpeedMultiplier","maxSpeedMultiplier","minRotationMultiplier","optRotationMultiplier","maxRotationMultiplier"]){const value=stats[field];if(value!==void 0)merged[field]=value*performanceRatio}}}const damageDistribution=effectiveDamageDistribution(module,stats);if(damageDistribution)merged.damageDistribution=damageDistribution;if(stats.category==="hardpoint")normalizeEffectiveWeapon(module,merged);if(convertsDamage(module)){delete merged.damageComponents}else if(stats.damageComponents){merged.damageComponents=scaleDamageComponents(stats.damageComponents,stats.damage,typeof merged.damage==="number"?merged.damage:void 0)}return merged}function powerConsumerFor(module,stats){const draw=effectiveStat(module,"powerDraw",stats);if(draw===void 0||draw===0)return null;const mounted=stats?.category==="hardpoint"||stats?.category==="utility";return{draw:draw,priority:priorityOf(module),enabled:isEnabled(module),deployedOnly:mounted&&stats?.alwaysPowered!==true,label:module.Slot,symbol:module.Item}}function powerAvailable(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(!isPowerPlant(module,stats))continue;if(!isEnabled(module))return 0;return effectiveStat(module,"powerCapacity",stats)??0}return 0}var DEFAULT_SLOT={powerCapacity:"PowerPlant",heatEfficiency:"PowerPlant",thrusters:"MainEngines",powerDistributor:"PowerDistributor"};var UNRESOLVED_FACT={thrusters:"thruster stats",shieldGenerator:"shield-generator stats",powerCapacity:"power capacity",heatEfficiency:"heat efficiency",powerDistributor:"power-distributor stats"};var NOTHING_FITTED={powerCapacity:"PowerPlant: no power plant is fitted",heatEfficiency:"PowerPlant: no power plant is fitted",thrusters:"MainEngines: no thrusters are fitted",powerDistributor:"PowerDistributor: no power distributor is fitted"};function metricIssue(field,reason,module){const defaultSlot=DEFAULT_SLOT[field];const slot=module?.Slot??defaultSlot;const symbol=module?.Item;const params={field:field,reason:reason,...slot===void 0?{}:{slot:slot},...symbol===void 0?{}:{symbol:symbol}};let message;if(reason==="missing"){message=NOTHING_FITTED[field]??"no shield generator is fitted"}else if(reason==="unresolved"){const unavailable=UNRESOLVED_FACT[field]??field;message=`${truncate(slot??field)}: ${unavailable} unavailable for ${truncate(symbol??field)}`}else if(reason==="disabled"){message=`${truncate(slot??field)}: ${truncate(symbol??field)} is switched off`}else if(reason==="invalid"){message=`${truncate(slot??field)}: ${truncate(symbol??field)} has invalid ${field}`}else{message=`${truncate(slot??field)}: ${truncate(symbol??field)} is not powered with hardpoints retracted`}return{field:field,reason:reason,...slot===void 0?{}:{slot:slot},...symbol===void 0?{}:{symbol:symbol},message:message,params:params}}function powerPlantIssueFor(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(!isPowerPlant(module,stats))continue;if(!isEnabled(module))return metricIssue("powerCapacity","disabled",module);const capacity=effectiveStat(module,"powerCapacity",stats);if(capacity===void 0)return metricIssue("powerCapacity","unresolved",module);return!Number.isFinite(capacity)||capacity<=0?metricIssue("powerCapacity","invalid",module):null}return metricIssue("powerCapacity","missing")}function powerDrawIssueFor(modules,statsFor){for(const module of modules){const consumer=powerConsumerFor(module,statsFor(module));if(consumer&&(!Number.isFinite(consumer.draw)||consumer.draw<0)){return metricIssue("powerDraw","invalid",module)}}return null}function poweredMetricBudgetFor(field,module,stats,modules,getBudget,statsFor){if(!isEnabled(module))return incompleteResult([metricIssue(field,"disabled",module)]);const plantIssue=powerPlantIssueFor(modules,statsFor);if(plantIssue)return incompleteResult([plantIssue]);const drawIssue=powerDrawIssueFor(modules,statsFor);if(drawIssue)return incompleteResult([drawIssue]);const budget=getBudget();return budget.available>0&&poweredStates(module,stats,budget).retracted?completeResult(budget):incompleteResult([metricIssue(field,"shed",module)])}function poweredShieldGeneratorResultFor(modules,getBudget,statsFor){for(const module of modules){if(!startsWithAny(module.Item,PREFIX.shieldGenerator))continue;const stats=statsFor(module);const powered=poweredMetricBudgetFor("shieldGenerator",module,stats,modules,getBudget,statsFor);if(!powered.complete)return powered;return stats===null?incompleteResult([metricIssue("shieldGenerator","unresolved",module)]):completeResult({module:module,budget:powered.value})}return incompleteResult([metricIssue("shieldGenerator","missing")])}function mapResistanceFields(read){return{kineticResistance:read("kineticResistance")??0,thermalResistance:read("thermalResistance")??0,explosiveResistance:read("explosiveResistance")??0,causticResistance:read("causticResistance")??0}}function resistancesOf(module,stats){return mapResistanceFields(field=>effectiveStat(module,field,stats))}function shieldInputFor(ship,modules,budget,statsFor){let generator=null;const boosters=[];let reinforcement=0;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(!(budget.available>0&&poweredStates(module,stats,budget).retracted))continue;if(!generator&&startsWithAny(module.Item,PREFIX.shieldGenerator)){const massRatio=modifierRatio(module,stats,"optMass");const strengthRatio=modifierRatio(module,stats,"optMultiplier");const optMass=effectiveStat(module,"optMass",stats);const minMass=relatedStat(module,stats,"minMass",massRatio);const maxMass=relatedStat(module,stats,"maxMass",Math.max(1,massRatio));const optMultiplier=effectiveStat(module,"optMultiplier",stats);const minMultiplier=relatedStat(module,stats,"minMultiplier",strengthRatio);const maxMultiplier=relatedStat(module,stats,"maxMultiplier",strengthRatio);generator={...optMass===void 0?{}:{optMass:optMass},...minMass===void 0?{}:{minMass:minMass},...maxMass===void 0?{}:{maxMass:maxMass},...optMultiplier===void 0?{}:{optMultiplier:optMultiplier},...minMultiplier===void 0?{}:{minMultiplier:minMultiplier},...maxMultiplier===void 0?{}:{maxMultiplier:maxMultiplier},...resistancesOf(module,stats)}}else if(startsWithAny(module.Item,PREFIX.shieldBooster)){boosters.push({shieldBoost:effectiveStat(module,"shieldBoost",stats)??0,...resistancesOf(module,stats)})}else if(startsWithAny(module.Item,PREFIX.shieldReinforcement)){reinforcement+=effectiveStat(module,"shieldAddition",stats)??0}}return{hullMass:ship.hullMass,baseShieldStrength:ship.baseShieldStrength,generator:generator,boosters:boosters,reinforcement:reinforcement}}function shieldInputResultFor(ship,modules,getBudget,statsFor){const fitted=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!fitted.complete)return fitted;return completeResult(shieldInputFor(ship,modules,fitted.value.budget,statsFor))}function shieldCapacitorInputResultFor(ship,modules,getBudget,systemsPips,statsFor){const fitted=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!fitted.complete)return fitted;const budget=fitted.value.budget;const capacitor=systemsCapacitorResultFor(modules,budget,statsFor);if(!capacitor.complete)return capacitor;const shields=shieldMetrics(shieldInputFor(ship,modules,budget,statsFor));return completeResult({strength:shields.strength,resistances:shields.resistances,systemsCapacity:capacitor.value.systemsCapacity,systemsRecharge:capacitor.value.systemsRecharge,systemsPips:systemsPips})}function systemsCapacitorResultFor(modules,budget,statsFor){let distributor=null;for(const module of modules){const stats2=statsFor(module);if(isPowerDistributor(module,stats2)&&isEnabled(module)&&budget.available>0&&poweredStates(module,stats2,budget).retracted)distributor=module}if(!distributor)return completeResult({systemsCapacity:0,systemsRecharge:0});const stats=statsFor(distributor);const systemsCapacity=effectiveStat(distributor,"systemsCapacity",stats);const systemsRecharge=effectiveStat(distributor,"systemsRecharge",stats);return systemsCapacity===void 0||systemsRecharge===void 0?incompleteResult([metricIssue("powerDistributor","unresolved",distributor)]):completeResult({systemsCapacity:systemsCapacity,systemsRecharge:systemsRecharge})}function thrusterCurveFor(module,stats){const effective=effectiveModule(module,stats);const minMass=effective?.minMass;const optMass=effective?.optMass;const maxMass=effective?.maxMass;const minMultiplier=effective?.minMultiplier;const optMultiplier=effective?.optMultiplier;const maxMultiplier=effective?.maxMultiplier;if(minMass===void 0||optMass===void 0||maxMass===void 0||minMultiplier===void 0||optMultiplier===void 0||maxMultiplier===void 0){return null}return{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:minMultiplier,optMultiplier:optMultiplier,maxMultiplier:maxMultiplier,...effective?.optSpeedMultiplier===void 0?{}:{speedCurve:{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:effective.minSpeedMultiplier??minMultiplier,optMultiplier:effective.optSpeedMultiplier,maxMultiplier:effective.maxSpeedMultiplier??maxMultiplier}},...effective?.optRotationMultiplier===void 0?{}:{rotationCurve:{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:effective.minRotationMultiplier??minMultiplier,optMultiplier:effective.optRotationMultiplier,maxMultiplier:effective.maxRotationMultiplier??maxMultiplier}}}}function fittedThrusterParamsFor(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(isThruster(module,stats))return thrusterCurveFor(module,stats)}return null}function mobilityInputResultFor(ship,modules,getBudget,mass,statsFor){let thrusters=null;for(const module of modules){const stats=statsFor(module);if(!isThruster(module,stats))continue;const powered=poweredMetricBudgetFor("thrusters",module,stats,modules,getBudget,statsFor);if(!powered.complete)return powered;thrusters=thrusterCurveFor(module,stats);if(!thrusters)return incompleteResult([metricIssue("thrusters","unresolved",module)]);break}if(!thrusters)return incompleteResult([metricIssue("thrusters","missing")]);return completeResult({minimumSpeed:ship.minimumSpeed,maximumSpeed:ship.maximumSpeed,boost:ship.boost,minPitch:ship.minPitch,pitch:ship.pitch,minRoll:ship.minRoll,roll:ship.roll,minYaw:ship.minYaw,yaw:ship.yaw,mass:typeof mass==="function"?mass():mass,thrusters:thrusters})}function shieldRecoveryInputResultFor(ship,modules,getBudget,systemsPips,statsFor){const generator=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!generator.complete)return generator;const{module:generatorModule,budget:budget}=generator.value;const generatorStats=statsFor(generatorModule);const distributorDraw=effectiveStat(generatorModule,"distributorDraw",generatorStats);const regenRate=effectiveStat(generatorModule,"shieldRegenRate",generatorStats);const brokenRegenRate=effectiveStat(generatorModule,"shieldBrokenRegenRate",generatorStats);if(distributorDraw===void 0||regenRate===void 0||brokenRegenRate===void 0){return incompleteResult([metricIssue("shieldGenerator","unresolved",generatorModule)])}const capacitor=systemsCapacitorResultFor(modules,budget,statsFor);if(!capacitor.complete)return capacitor;const strength=shieldMetrics(shieldInputFor(ship,modules,budget,statsFor)).strength;return completeResult({strength:strength,regenRate:regenRate,brokenRegenRate:brokenRegenRate,distributorDraw:distributorDraw,systemsCapacity:capacitor.value.systemsCapacity,systemsRecharge:capacitor.value.systemsRecharge,systemsPips:systemsPips})}function weaponsCapacitorInputFor(modules,weaponsPips,budget,statsFor){let weaponsCapacity=0;let weaponsRecharge=0;let sustainedEnergyPerSecond=0;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(!poweredStates(module,stats,budget).deployed)continue;if(isPowerDistributor(module,stats)){weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats)??0;weaponsRecharge=effectiveStat(module,"weaponsRecharge",stats)??0}const weapon=weaponStatsFor(module,stats);if(weapon){sustainedEnergyPerSecond+=weaponMetrics(weapon).sustainedEnergyPerSecond}}return{weaponsCapacity:weaponsCapacity,weaponsRecharge:weaponsRecharge,sustainedEnergyPerSecond:sustainedEnergyPerSecond,weaponsPips:weaponsPips}}function distributorInputResultFor(modules,pips,budget,statsFor){let disabled=null;for(const module of modules){const stats=statsFor(module);if(!isPowerDistributor(module,stats))continue;if(!isEnabled(module)){disabled??=module;continue}if(!poweredStates(module,stats,budget).retracted){return incompleteResult([metricIssue("powerDistributor","shed",module)])}const systemsCapacity=effectiveStat(module,"systemsCapacity",stats);const systemsRecharge=effectiveStat(module,"systemsRecharge",stats);const enginesCapacity=effectiveStat(module,"enginesCapacity",stats);const enginesRecharge=effectiveStat(module,"enginesRecharge",stats);const weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats);const weaponsRecharge=effectiveStat(module,"weaponsRecharge",stats);if(systemsCapacity===void 0||systemsRecharge===void 0||enginesCapacity===void 0||enginesRecharge===void 0||weaponsCapacity===void 0||weaponsRecharge===void 0){return incompleteResult([metricIssue("powerDistributor","unresolved",module)])}return completeResult({systemsCapacity:systemsCapacity,systemsRecharge:systemsRecharge,enginesCapacity:enginesCapacity,enginesRecharge:enginesRecharge,weaponsCapacity:weaponsCapacity,weaponsRecharge:weaponsRecharge,...pips})}return incompleteResult([disabled===null?metricIssue("powerDistributor","missing"):metricIssue("powerDistributor","disabled",disabled)])}function heatInputResultFor(ship,modules,budget,statsFor){let plant=null;let heatEfficiency;let thrusterHeatRate=0;let deployedThrusterHeatRate=0;let fsdHeatRate=0;let weaponsCapacity=0;const weapons=[];for(const module of modules){const stats=statsFor(module);if(isPowerPlant(module,stats)){if(!isEnabled(module)){return incompleteResult([metricIssue("powerCapacity","disabled",module)])}plant=module;heatEfficiency=effectiveStat(module,"heatEfficiency",stats);continue}if(!isEnabled(module))continue;const running=poweredStates(module,stats,budget);if(isThruster(module,stats)){const heat=effectiveStat(module,"engineHeatRate",stats)??0;if(running.retracted)thrusterHeatRate+=heat;if(running.deployed)deployedThrusterHeatRate+=heat}else if(isFrameShiftDrive(module,stats)){if(running.retracted)fsdHeatRate+=effectiveStat(module,"fsdHeatRate",stats)??0}else if(isPowerDistributor(module,stats)){if(running.deployed){weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats)??0}}const weapon=weaponStatsFor(module,stats);if(!running.deployed)continue;if(!weapon)continue;weapons.push({heatPerSecond:weaponMetrics(weapon).sustainedHeatPerSecond,distributorDraw:weapon.distributorDraw??0})}if(heatEfficiency===void 0){return incompleteResult([plant===null?metricIssue("powerCapacity","missing"):metricIssue("heatEfficiency","unresolved",plant)])}return completeResult({heatCapacity:ship.heatCapacity,heatDissipation:ship.heatDissipation,heatEfficiency:heatEfficiency,retractedPowerDraw:poweredDraw(budget,"retracted"),deployedPowerDraw:poweredDraw(budget,"deployed"),thrusterHeatRate:thrusterHeatRate,deployedThrusterHeatRate:deployedThrusterHeatRate,fsdHeatRate:fsdHeatRate,weaponsCapacity:weaponsCapacity,weapons:weapons})}function poweredDraw(budget,state){const powered=state==="retracted"?"poweredRetracted":"poweredDeployed";return budget.bands.reduce((total,band)=>band[powered]?total+band[state]:total,0)}function poweredStates(module,stats,budget){const draw=effectiveStat(module,"powerDraw",stats);if(draw===void 0||draw===0)return{retracted:true,deployed:true};const band=budget.bands[Math.min(budget.bands.length,Math.max(1,priorityOf(module)))-1];if(!band)return{retracted:true,deployed:true};return{retracted:band.poweredRetracted,deployed:band.poweredDeployed}}function cellBankInputsFor(modules,budget,statsFor){const banks=[];for(const module of modules){if(!startsWithAny(module.Item,PREFIX.shieldCellBank))continue;const stats=statsFor(module);const effective=effectiveModule(module,stats);const ammunition=effective?ammunitionCapacity(effective):null;banks.push({slot:module.Slot,symbol:module.Item,reinforcementRate:effective?.shieldBankReinforcement??0,cells:ammunition?.total??0,spinUp:effective?.shieldBankSpinUp??0,duration:effective?.shieldBankDuration??0,heat:effective?.shieldBankHeat??0,powered:budget.available>0&&isEnabled(module)&&poweredStates(module,stats,budget).deployed})}return banks}function stockBulkhead(ship){const variants=getBulkheadsForShip(ship.name,CORE_MODULES);return variants.find(v=>v.symbol.toLowerCase().endsWith("_default"))??variants.find(v=>v.mass===0)??variants[0]??null}function armourInputFor(ship,modules,statsFor){const reinforcements=[];const moduleReinforcements=[];let bulkhead=null;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(module.Slot.toLowerCase()==="armour"){bulkhead={hullBoost:effectiveStat(module,"hullBoost",stats)??0,...resistancesOf(module,stats)}}else if(startsWithAny(module.Item,PREFIX.hullReinforcement)){const boost=getLoadoutModifier(module,"DefenceModifierHealthMultiplier");reinforcements.push({hullReinforcement:effectiveStat(module,"hullReinforcement",stats)??0,hullBoost:boost===null?0:boost/scaleForLabel("DefenceModifierHealthMultiplier"),...resistancesOf(module,stats)})}else if(startsWithAny(module.Item,PREFIX.moduleReinforcement)){moduleReinforcements.push({moduleProtection:effectiveStat(module,"moduleProtection",stats)??0,integrity:effectiveStat(module,"integrity",stats)??0})}}if(!bulkhead){const stock=stockBulkhead(ship);bulkhead=stock?{hullBoost:stock.hullBoost??0,...mapResistanceFields(field=>stock[field])}:null}return{baseArmour:ship.baseArmour,bulkhead:bulkhead,reinforcements:reinforcements,moduleReinforcements:moduleReinforcements}}var WEAPON_FIELDS=["damage","roundsPerShot","rateOfFire","burstInterval","burstRounds","burstRateOfFire","chargeTime","clipSize","ammoMaximum","reloadTime","distributorDraw","thermalLoad","powerDraw","maximumRange","falloffRange","armourPiercing"];function weaponStatsFor(module,stats){if(!stats||stats.category!=="hardpoint")return null;const weapon={};for(const field of WEAPON_FIELDS){const value=effectiveStat(module,field,stats);if(value!==void 0)weapon[field]=value}normalizeEffectiveWeapon(module,weapon);const damageDistribution=effectiveDamageDistribution(module,stats);if(damageDistribution)weapon.damageDistribution=damageDistribution;if(stats.damageComponents&&!convertsDamage(module)){weapon.damageComponents=scaleDamageComponents(stats.damageComponents,stats.damage,typeof weapon.damage==="number"?weapon.damage:void 0)}if(stats.projectileRange)weapon.projectileRange={...stats.projectileRange};return weapon}function burstAdjustedRateOfFire(module,weapon){if(getLoadoutModifier(module,"RateOfFire")!==null)return void 0;const touched=["BurstSize","BurstRateOfFire","BurstInterval"].some(label=>getLoadoutModifier(module,label)!==null);if(!touched)return void 0;return combinedRateOfFire(weapon)}function normalizeEffectiveWeapon(module,weapon){const rate=burstAdjustedRateOfFire(module,weapon);if(rate!==void 0)weapon.rateOfFire=rate;const statedDamagePerSecond=getLoadoutModifier(module,"DamagePerSecond");const firingFactor=Number(weapon.roundsPerShot??1)*Number(weapon.rateOfFire??1);if(statedDamagePerSecond!==null&&firingFactor>0){weapon.damage=statedDamagePerSecond/firingFactor}const{maximumRange:maximumRange,falloffRange:falloffRange}=weapon;if(maximumRange!==void 0&&falloffRange!==void 0&&falloffRange>maximumRange){weapon.falloffRange=maximumRange}}var INTERNALS=/* @__PURE__ */new WeakMap;function publishLoadoutInternals(build,internals){INTERNALS.set(build,internals)}function loadoutInternals(build,label){const internals=typeof build==="object"&&build!==null?INTERNALS.get(build):void 0;if(internals===void 0){throw new TypeError(`${label} must be a ShipLoadout, received ${describeValue(build)}`)}return internals}export{armourInputFor,cellBankInputsFor,completeResult,distributorInputResultFor,effectiveModule,fittedThrusterParamsFor,heatInputResultFor,incompleteResult,loadoutInternals,mobilityInputResultFor,moduleFitError,moduleFitProblem,normalizeLoadoutEvent,powerAvailable,powerConsumerFor,preEngineeredVariantFor,publishLoadoutInternals,shieldCapacitorInputResultFor,shieldInputResultFor,shieldRecoveryInputResultFor,weaponStatsFor,weaponsCapacitorInputFor};//# sourceMappingURL=chunk-AAE4KZ2V.js.map
|
|
1
|
+
import{shieldMetrics}from"./chunk-5VWACBTN.js";import{getShipBySymbol,getShipByName,getShipSlots}from"./chunk-6UNAD74Z.js";import{getLoadoutModifier}from"./chunk-T4MKUKYS.js";import{sourcePurchaseFromLoadout}from"./chunk-NIURLW2F.js";import{identifyPreEngineeredVariant,unrollableFixedArticle,cloneLoadoutModule,isNonOutfittingSlot,isBuiltInHullModule,matchingKeyIn,ownKeyIn,getPreEngineeredStats,getPreEngineeredModifiers,cloneModuleStats,isFinalGuardianWeaponEngineering,scaleDamageComponents}from"./chunk-DABPLBTN.js";import{weaponMetrics,combinedRateOfFire}from"./chunk-37JIMWLK.js";import{builtInModuleBySymbol,getBulkheadsForShip}from"./chunk-3LZXB2MC.js";import{getPreEngineeredVariants}from"./chunk-KPJ77VPS.js";import{CORE_MODULES}from"./chunk-FA5JVXVG.js";import{getExperimentalEffect}from"./chunk-77I24V6B.js";import{getDefaultLoadout}from"./chunk-GRIK3EDH.js";import{fieldForLabel,scaleForLabel,labelsForField,capabilityValueForLabel,damageTypeForLabel}from"./chunk-CJQ37JII.js";import{ammunitionCapacity}from"./chunk-AA3K5XUE.js";import{parseSlotName,SLOT_RESTRICTION_LABELS,enumerateSlots}from"./chunk-RAO35XDF.js";import{normalizeKey}from"./chunk-BKWLI44T.js";import{truncate,requireString,describeValue,requireStringIfPresent}from"./chunk-PKWVG2JY.js";import{deepFreeze}from"./chunk-DURKO7JU.js";var MILITARY_PREFIXES=["int_hullreinforcement","int_metaalloyhullreinforcement","int_modulereinforcement","int_shieldcellbank","int_guardianhullreinforcement","int_guardianmodulereinforcement","int_guardianshieldreinforcement"];var MINING_PREFIXES=["hpt_mininglaser","hpt_mining_abrblstr","hpt_mining_seismchrgwarhd","hpt_mining_subsurfdispmisle","hpt_human_extraction","hpt_miningtoolv2"];var CARGO_PREFIXES=["int_cargorack","int_largecargorack","int_corrosionproofcargorack","int_fueltank"];var RESTRICTED_SLOT_PREFIXES={mining:MINING_PREFIXES,military:MILITARY_PREFIXES,cargo:CARGO_PREFIXES,limpetController:["int_dronecontrol","int_multidronecontrol"],vesselHangar:["int_fighterbay"],passenger:["int_passengercabin","int_mkii_passengercabin"],planetaryApproachSuite:["int_planetapproachsuite"]};var problem=(constraint,message,params)=>({constraint:constraint,message:message,...params===void 0?{}:{params:params}});function restrictionProblem(slot,symbol){const restriction=slot.restriction;if(!restriction)return null;if(RESTRICTED_SLOT_PREFIXES[restriction].some(prefix=>symbol.startsWith(prefix))){return null}return problem("restrictedMount",`slot only takes ${SLOT_RESTRICTION_LABELS[restriction]}`,{restriction:restriction})}function moduleSlotProblem(slot,module){const required=module.restrictedToSlot;if(!required||slot.restriction===required)return null;return problem("restrictedMount",`module only fits a mount that takes ${SLOT_RESTRICTION_LABELS[required]}`,{restriction:required})}function moduleFitProblem(shipSymbol,slot,module){if(slot.kind==="cargoHatch"){return problem("immutableSlot","the cargoHatch slot cannot be changed")}if(slot.kind==="armour"){const hull=getShipBySymbol(shipSymbol);if(module.slot!=="armour"||module.ship===void 0){return problem("armourRequired","not a ship armour module")}if(!hull||module.ship.toLowerCase()!==hull.name.toLowerCase()){const armourHull=getShipByName(module.ship);return problem("wrongHullArmour",`armour belongs to ${truncate(module.ship)}, not ${truncate(hull?.name??shipSymbol)}`,{armourShipName:module.ship,...armourHull===null?{}:{armourShipSymbol:armourHull.symbol},shipSymbol:shipSymbol,...hull===null?{}:{shipName:hull.name}})}return null}const restricted=module.restrictedToShips;if(restricted&&!restricted.some(symbol2=>symbol2.toLowerCase()===shipSymbol.toLowerCase())){const allowedShipNames=restricted.map(symbol2=>{const hull=getShipBySymbol(symbol2);return hull?.name??symbol2});const labels=restricted.map((symbol2,index)=>{const name=allowedShipNames[index];return name===symbol2?symbol2:`${name} (${symbol2})`});return problem("restrictedHull",`module is restricted to ${truncate(labels.join(", "))}`,{allowedShipNames:allowedShipNames,allowedShipSymbols:[...restricted],shipSymbol:shipSymbol})}const wrongMount=moduleSlotProblem(slot,module);if(wrongMount)return wrongMount;const symbol=module.symbol.toLowerCase();const moduleSlot=module.slot;switch(slot.kind){case"core":if(moduleSlot!==slot.core){return problem("wrongCoreType",`not a ${slot.core} module`,{requiredCore:slot.core,moduleSlot:moduleSlot??"none"})}break;case"hardpoint":{if(module.category!=="hardpoint"){return problem("hardpointRequired","not a hardpoint weapon")}const restricted2=restrictionProblem(slot,symbol);if(restricted2)return restricted2;break}case"utility":if(module.category!=="utility"){return problem("utilityRequired","not a utility module")}return null;case"optional":{const isFuelTank=moduleSlot==="fuelTank";if(module.category!=="internal"&&!isFuelTank){return problem("optionalInternalRequired","not an optional-internal module")}if(moduleSlot&&!isFuelTank){return problem("coreModuleInOptionalSlot","a core module only fits its core slot")}const restricted2=restrictionProblem(slot,symbol);if(restricted2)return restricted2;break}}return module.class>slot.size?problem("oversized",`module size ${truncate(module.class)} exceeds slot size ${slot.size}`,{moduleClass:module.class,slotSize:slot.size}):null}function moduleFitError(shipSymbol,slot,module){return moduleFitProblem(shipSymbol,slot,module)?.message??null}function preEngineeredVariantFor(module){return identifyPreEngineeredVariant(module)??(module.Engineering?unrollableFixedArticle(module.Item,module.Engineering):null)}function captureLoadoutEvent(event){const modules=event.Modules;const shipName=event.ShipName;const shipIdent=event.ShipIdent;const hullValue=event.HullValue;const modulesValue=event.ModulesValue;const rebuy=event.Rebuy;const unladenMass=event.UnladenMass;const cargoCapacity=event.CargoCapacity;const fuelCapacity=event.FuelCapacity;return{Ship:event.Ship,Modules:Array.isArray(modules)?captureEach(modules,captureModule):modules,...shipName===void 0?{}:{ShipName:shipName},...shipIdent===void 0?{}:{ShipIdent:shipIdent},...hullValue===void 0?{}:{HullValue:hullValue},...modulesValue===void 0?{}:{ModulesValue:modulesValue},...rebuy===void 0?{}:{Rebuy:rebuy},...unladenMass===void 0?{}:{UnladenMass:unladenMass},...cargoCapacity===void 0?{}:{CargoCapacity:cargoCapacity},...fuelCapacity===void 0?{}:{FuelCapacity:{...fuelCapacity}}}}function captureEach(values,capture){const captured=[];const{length:length}=values;for(let index=0;index<length;index++)captured.push(capture(values[index]));return captured}function captureModule(module){if(module===null||typeof module!=="object")return module;const on=module.On;const priority=module.Priority;const health=module.Health;const value=module.Value;const engineering=module.Engineering;return{Slot:module.Slot,Item:module.Item,...on===void 0?{}:{On:on},...priority===void 0?{}:{Priority:priority},...health===void 0?{}:{Health:health},...value===void 0?{}:{Value:value},...engineering===void 0?{}:{Engineering:captureEngineering(engineering)}}}function captureEngineering(engineering){if(engineering===null||typeof engineering!=="object")return engineering;const experimental=engineering.ExperimentalEffect;const experimentalLocalised=engineering.ExperimentalEffect_Localised;const modifiers=engineering.Modifiers;return{BlueprintName:engineering.BlueprintName,Level:engineering.Level,Quality:engineering.Quality,...experimental===void 0?{}:{ExperimentalEffect:experimental},...experimentalLocalised===void 0?{}:{ExperimentalEffect_Localised:experimentalLocalised},...modifiers===void 0?{}:{Modifiers:Array.isArray(modifiers)?captureEach(modifiers,modifier=>modifier!==null&&typeof modifier==="object"?{...modifier}:modifier):modifiers}}}function hullLayout(shipSymbol){const slots=getShipSlots(shipSymbol);return slots?enumerateSlots(slots):[]}function fixedMountRejects(shipSymbol,layout,slot,stats){const mount=layout.find(candidate=>candidate.key.toLowerCase()===slot.toLowerCase());return mount!==void 0&&moduleFitProblem(shipSymbol,mount,stats)!==null}function normalizeLoadoutEvent(rawEvent){const event=captureLoadoutEvent(rawEvent);requireString(event.Ship,"ShipLoadout.fromLoadout: event.Ship");if(!Array.isArray(event.Modules)){throw new TypeError(`ShipLoadout.fromLoadout: event.Modules must be an array, received ${describeValue(event.Modules)}`)}const modules=/* @__PURE__ */new Map;const slots=/* @__PURE__ */new Set;for(const module of event.Modules){if(module===null||typeof module!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: event.Modules[] must hold module objects, received ${describeValue(module)}`)}requireString(module.Slot,"ShipLoadout.fromLoadout: module.Slot");requireString(module.Item,"ShipLoadout.fromLoadout: module.Item");if(module.Engineering!==void 0){if(module.Engineering===null||typeof module.Engineering!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering must be an object, received ${describeValue(module.Engineering)}`)}requireStringIfPresent(module.Engineering.BlueprintName,"ShipLoadout.fromLoadout: module.Engineering.BlueprintName");requireStringIfPresent(module.Engineering.ExperimentalEffect,"ShipLoadout.fromLoadout: module.Engineering.ExperimentalEffect");if(module.Engineering.Modifiers!==void 0&&!Array.isArray(module.Engineering.Modifiers)){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering.Modifiers must be an array, received ${describeValue(module.Engineering.Modifiers)}`)}for(const[index,modifier]of(module.Engineering.Modifiers??[]).entries()){if(modifier===null||typeof modifier!=="object"){throw new TypeError(`ShipLoadout.fromLoadout: module.Engineering.Modifiers[${index}] must be an object, received ${describeValue(modifier)}`)}requireString(modifier.Label,`ShipLoadout.fromLoadout: module.Engineering.Modifiers[${index}].Label`)}}const normalizedSlot=module.Slot.toLowerCase();if(slots.has(normalizedSlot)){throw new TypeError(`ShipLoadout.fromLoadout: duplicate slot "${truncate(module.Slot)}"`)}slots.add(normalizedSlot);modules.set(module.Slot,cloneLoadoutModule(module))}const top={};if(event.ShipName!==void 0)top.ShipName=event.ShipName;if(event.ShipIdent!==void 0)top.ShipIdent=event.ShipIdent;if(event.HullValue!==void 0)top.HullValue=event.HullValue;if(event.ModulesValue!==void 0)top.ModulesValue=event.ModulesValue;if(event.Rebuy!==void 0)top.Rebuy=event.Rebuy;if(event.UnladenMass!==void 0)top.UnladenMass=event.UnladenMass;if(event.CargoCapacity!==void 0)top.CargoCapacity=event.CargoCapacity;if(event.FuelCapacity!==void 0)top.FuelCapacity={...event.FuelCapacity};let invalidatesAggregates=false;const outcomes=[];const defaults=getDefaultLoadout(event.Ship)?.modules??[];let layout;for(const[slot,module]of modules){if(isNonOutfittingSlot(slot)||isBuiltInHullModule(module))continue;const stats=builtInModuleBySymbol(module.Item,"ShipLoadout.fromLoadout: module.Item");const slotKind=parseSlotName(slot)?.kind;const fallback=slotKind==="core"||slotKind==="armour"||slotKind==="cargoHatch"?defaults.find(candidate=>candidate.slot.toLowerCase()===slot.toLowerCase()):void 0;const rejected=fallback!==void 0&&(stats===null||slotKind==="cargoHatch"||fixedMountRejects(event.Ship,layout??=hullLayout(event.Ship),slot,stats));if(stats!==null&&!rejected)continue;if(fallback){modules.set(slot,{Slot:slot,Item:fallback.symbol,...module.On===void 0?{}:{On:module.On},...module.Priority===void 0?{}:{Priority:module.Priority},...module.Health===void 0?{}:{Health:module.Health}});outcomes.push({action:"defaulted",slot:slot,sourceSymbol:module.Item,replacementSymbol:fallback.symbol})}else{modules.delete(slot);outcomes.push({action:"emptied",slot:slot,sourceSymbol:module.Item})}invalidatesAggregates=true}for(const fallback of defaults){const slotKind=parseSlotName(fallback.slot)?.kind;if(slotKind!=="core"&&slotKind!=="armour"&&slotKind!=="cargoHatch"||matchingKeyIn(modules,fallback.slot)!==null){continue}const slot=ownKeyIn(modules,fallback.slot);modules.set(slot,{Slot:slot,Item:fallback.symbol});outcomes.push({action:"defaulted",slot:slot,sourceSymbol:null,replacementSymbol:fallback.symbol});if(slotKind==="core")invalidatesAggregates=true}if(invalidatesAggregates){delete top.ModulesValue;delete top.Rebuy;delete top.UnladenMass;delete top.CargoCapacity;delete top.FuelCapacity}const moduleStats=/* @__PURE__ */new Map;const primitiveModifiers=/* @__PURE__ */new Map;for(const module of modules.values()){const engineering=module.Engineering;const variant=preEngineeredVariantFor(module);const catalogueVariantStats=variant?getPreEngineeredStats(variant):null;let variantStats=catalogueVariantStats;const retainsBakedExperimental=variant?.experimentalEffectSymbol!==void 0&&typeof engineering?.ExperimentalEffect==="string"&&variant.experimentalEffectSymbol.trim().toLowerCase()===engineering.ExperimentalEffect.trim().toLowerCase();if(variant&&catalogueVariantStats&&!catalogueVariantStats.engineeringLocked&&!retainsBakedExperimental){const{experimentalEffectSymbol:originalExperimental,...withoutExperimental}=variant;const currentExperimental=engineering?.ExperimentalEffect;const currentVariant=typeof currentExperimental==="string"?{...withoutExperimental,experimentalEffectSymbol:currentExperimental}:withoutExperimental;variantStats=getPreEngineeredStats(withoutExperimental);primitiveModifiers.set(module.Slot,[...engineering?.Modifiers??[],...getPreEngineeredModifiers(currentVariant)])}if(variantStats)moduleStats.set(module.Slot,cloneModuleStats(variantStats));const guardianFinal=typeof engineering?.BlueprintName==="string"&&isFinalGuardianWeaponEngineering(module.Item,engineering.BlueprintName);if(variantStats?.engineeringLocked||typeof engineering?.BlueprintName!=="string"||!guardianFinal){continue}const normalizedBlueprint=normalizeKey(engineering.BlueprintName,"ShipLoadout.fromLoadout: module.Engineering.BlueprintName");const normalizedExperimental=normalizeKey(engineering.ExperimentalEffect,"ShipLoadout.fromLoadout: module.Engineering.ExperimentalEffect");const variants=getPreEngineeredVariants(module.Item);const exact=variants.find(candidate=>candidate.engineeringLocked===true&&candidate.blueprintSymbol.toLowerCase()===normalizedBlueprint&&candidate.grade===engineering.Level&&candidate.experimentalEffectSymbol?.toLowerCase()===normalizedExperimental);const guardianBase=variants.find(candidate=>candidate.engineeringLocked===true&&candidate.blueprintSymbol.toLowerCase()===normalizedBlueprint&&candidate.grade===engineering.Level&&candidate.experimentalEffectSymbol===void 0);const stats=exact?getPreEngineeredStats(exact):guardianBase&&engineering.ExperimentalEffect!==void 0?getPreEngineeredStats({...guardianBase,experimentalEffectSymbol:engineering.ExperimentalEffect}):builtInModuleBySymbol(module.Item,"ShipLoadout.fromLoadout: module.Item");if(stats){moduleStats.set(module.Slot,cloneModuleStats({...stats,engineeringLocked:true}))}}return{shipSymbol:event.Ship,modules:modules,moduleStats:moduleStats,primitiveModifiers:primitiveModifiers,top:top,sourcePurchase:sourcePurchaseFromLoadout(event),outcomes:deepFreeze(outcomes)}}var NO_ISSUES=Object.freeze([]);function completeResult(value){return Object.freeze({value:value,complete:true,issues:NO_ISSUES})}function incompleteResult(issues){return Object.freeze({value:null,complete:false,issues:Object.freeze(issues.map(issue=>Object.freeze({...issue,...issue.params?{params:Object.freeze({...issue.params})}:{}})))})}var PREFIX={powerPlant:["int_powerplant","int_guardianpowerplant"],thrusters:["int_engine","int_mkiiagileboost_engine"],frameShiftDrive:["int_hyperdrive"],powerDistributor:["int_powerdistributor","int_guardianpowerdistributor"],shieldGenerator:["int_shieldgenerator"],shieldCellBank:["int_shieldcellbank"],shieldBooster:["hpt_shieldbooster"],shieldReinforcement:["int_guardianshieldreinforcement"],hullReinforcement:["int_hullreinforcement","int_guardianhullreinforcement","int_metaalloyhullreinforcement"],moduleReinforcement:["int_modulereinforcement","int_guardianmodulereinforcement"]};var startsWithAny=(symbol,prefixes)=>prefixes.some(prefix=>symbol.toLowerCase().startsWith(prefix));function isPowerDistributor(module,stats){return stats?.slot?stats.slot==="powerDistributor":startsWithAny(module.Item,PREFIX.powerDistributor)}function isPowerPlant(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="powerPlant")return true;return stats?.slot?stats.slot==="powerPlant":startsWithAny(module.Item,PREFIX.powerPlant)}function isThruster(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="thrusters")return true;return stats?.slot?stats.slot==="thrusters":startsWithAny(module.Item,PREFIX.thrusters)}function isFrameShiftDrive(module,stats){const parsed=parseSlotName(module.Slot);if(parsed?.kind==="core"&&parsed.core==="frameShiftDrive")return true;return stats?.slot?stats.slot==="frameShiftDrive":startsWithAny(module.Item,PREFIX.frameShiftDrive)}function isEnabled(module){return module.On!==false}function priorityOf(module){return(module.Priority??0)+1}function effectiveStat(module,field,stats){const stated=statedModifier(module,field);if(stated!==void 0)return stated;const base=stats?.[field];return typeof base==="number"?base:void 0}function statedModifier(module,field){for(const label of labelsForField(field)){if(capabilityValueForLabel(label)!==null)continue;const modified=getLoadoutModifier(module,label);if(modified!==null)return modified/scaleForLabel(label)}return void 0}function effectiveCapability(module,field,stats){for(const label of labelsForField(field)){if(capabilityValueForLabel(label)===null)continue;if(module.Engineering?.Modifiers?.some(modifier=>modifier.Label.toLowerCase()===label.toLowerCase())){return true}}const base=stats[field];return typeof base==="boolean"?base:void 0}function effectiveDamageDistribution(module,stats){const fromEffect=module.Engineering?.ExperimentalEffect?getExperimentalEffect(module.Engineering.ExperimentalEffect)?.damageDistribution:void 0;const distribution={...fromEffect??stats.damageDistribution};let resolved=fromEffect!==void 0||stats.damageDistribution!==void 0;const seen=/* @__PURE__ */new Set;for(const modifier of module.Engineering?.Modifiers??[]){const type=damageTypeForLabel(modifier.Label);if(type===null||seen.has(type))continue;seen.add(type);if(modifier.Value===void 0)continue;distribution[type]=modifier.Value/scaleForLabel(modifier.Label);resolved=true}return resolved?distribution:void 0}function convertsDamage(module){if(module.Engineering?.ExperimentalEffect&&getExperimentalEffect(module.Engineering.ExperimentalEffect)?.damageDistribution){return true}return(module.Engineering?.Modifiers??[]).some(modifier=>damageTypeForLabel(modifier.Label)!==null)}function modifierRatio(module,stats,field){const base=stats?.[field];if(typeof base!=="number"||base===0)return 1;const effective=effectiveStat(module,field,stats);return effective===void 0?1:effective/base}function relatedStat(module,stats,field,ratio){const stated=statedModifier(module,field);if(stated!==void 0)return stated;const base=stats?.[field];return typeof base==="number"?base*ratio:void 0}function effectiveModule(module,stats){if(!stats||!module.Engineering)return stats;const merged={...stats};const fields=new Set(Object.keys(stats));for(const modifier of module.Engineering.Modifiers??[]){const field=fieldForLabel(modifier.Label,stats);if(field)fields.add(field)}for(const key of fields){const capability=effectiveCapability(module,key,stats);if(capability!==void 0){merged[key]=capability;continue}const value=effectiveStat(module,key,stats);if(value!==void 0)merged[key]=value}if(stats.engineeringGroup==="thrusters"){const massRatio=modifierRatio(module,stats,"optMass");const minMass=relatedStat(module,stats,"minMass",massRatio);const maxMass=relatedStat(module,stats,"maxMass",massRatio);if(minMass!==void 0)merged.minMass=minMass;if(maxMass!==void 0)merged.maxMass=maxMass}if(stats.engineeringGroup==="thrusters"||stats.engineeringGroup==="shieldGenerators"){const performanceRatio=modifierRatio(module,stats,"optMultiplier");const minMultiplier=relatedStat(module,stats,"minMultiplier",performanceRatio);const maxMultiplier=relatedStat(module,stats,"maxMultiplier",performanceRatio);if(minMultiplier!==void 0)merged.minMultiplier=minMultiplier;if(maxMultiplier!==void 0)merged.maxMultiplier=maxMultiplier;if(stats.engineeringGroup==="thrusters"){for(const field of["minSpeedMultiplier","optSpeedMultiplier","maxSpeedMultiplier","minRotationMultiplier","optRotationMultiplier","maxRotationMultiplier"]){const value=stats[field];if(value!==void 0)merged[field]=value*performanceRatio}}}const damageDistribution=effectiveDamageDistribution(module,stats);if(damageDistribution)merged.damageDistribution=damageDistribution;if(stats.category==="hardpoint")normalizeEffectiveWeapon(module,merged);if(convertsDamage(module)){delete merged.damageComponents}else if(stats.damageComponents){merged.damageComponents=scaleDamageComponents(stats.damageComponents,stats.damage,typeof merged.damage==="number"?merged.damage:void 0)}return merged}function powerConsumerFor(module,stats){const draw=effectiveStat(module,"powerDraw",stats);if(draw===void 0||draw===0)return null;const mounted=stats?.category==="hardpoint"||stats?.category==="utility";return{draw:draw,priority:priorityOf(module),enabled:isEnabled(module),deployedOnly:mounted&&stats?.alwaysPowered!==true,label:module.Slot,symbol:module.Item}}function powerAvailable(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(!isPowerPlant(module,stats))continue;if(!isEnabled(module))return 0;return effectiveStat(module,"powerCapacity",stats)??0}return 0}var DEFAULT_SLOT={powerCapacity:"PowerPlant",heatEfficiency:"PowerPlant",thrusters:"MainEngines",powerDistributor:"PowerDistributor"};var UNRESOLVED_FACT={thrusters:"thruster stats",shieldGenerator:"shield-generator stats",powerCapacity:"power capacity",heatEfficiency:"heat efficiency",powerDistributor:"power-distributor stats"};var NOTHING_FITTED={powerCapacity:"PowerPlant: no power plant is fitted",heatEfficiency:"PowerPlant: no power plant is fitted",thrusters:"MainEngines: no thrusters are fitted",powerDistributor:"PowerDistributor: no power distributor is fitted"};function metricIssue(field,reason,module){const defaultSlot=DEFAULT_SLOT[field];const slot=module?.Slot??defaultSlot;const symbol=module?.Item;const params={field:field,reason:reason,...slot===void 0?{}:{slot:slot},...symbol===void 0?{}:{symbol:symbol}};let message;if(reason==="missing"){message=NOTHING_FITTED[field]??"no shield generator is fitted"}else if(reason==="unresolved"){const unavailable=UNRESOLVED_FACT[field]??field;message=`${truncate(slot??field)}: ${unavailable} unavailable for ${truncate(symbol??field)}`}else if(reason==="disabled"){message=`${truncate(slot??field)}: ${truncate(symbol??field)} is switched off`}else if(reason==="invalid"){message=`${truncate(slot??field)}: ${truncate(symbol??field)} has invalid ${field}`}else{message=`${truncate(slot??field)}: ${truncate(symbol??field)} is not powered with hardpoints retracted`}return{field:field,reason:reason,...slot===void 0?{}:{slot:slot},...symbol===void 0?{}:{symbol:symbol},message:message,params:params}}function powerPlantIssueFor(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(!isPowerPlant(module,stats))continue;if(!isEnabled(module))return metricIssue("powerCapacity","disabled",module);const capacity=effectiveStat(module,"powerCapacity",stats);if(capacity===void 0)return metricIssue("powerCapacity","unresolved",module);return!Number.isFinite(capacity)||capacity<=0?metricIssue("powerCapacity","invalid",module):null}return metricIssue("powerCapacity","missing")}function powerDrawIssueFor(modules,statsFor){for(const module of modules){const consumer=powerConsumerFor(module,statsFor(module));if(consumer&&(!Number.isFinite(consumer.draw)||consumer.draw<0)){return metricIssue("powerDraw","invalid",module)}}return null}function poweredMetricBudgetFor(field,module,stats,modules,getBudget,statsFor){if(!isEnabled(module))return incompleteResult([metricIssue(field,"disabled",module)]);const plantIssue=powerPlantIssueFor(modules,statsFor);if(plantIssue)return incompleteResult([plantIssue]);const drawIssue=powerDrawIssueFor(modules,statsFor);if(drawIssue)return incompleteResult([drawIssue]);const budget=getBudget();return budget.available>0&&poweredStates(module,stats,budget).retracted?completeResult(budget):incompleteResult([metricIssue(field,"shed",module)])}function poweredShieldGeneratorResultFor(modules,getBudget,statsFor){for(const module of modules){if(!startsWithAny(module.Item,PREFIX.shieldGenerator))continue;const stats=statsFor(module);const powered=poweredMetricBudgetFor("shieldGenerator",module,stats,modules,getBudget,statsFor);if(!powered.complete)return powered;return stats===null?incompleteResult([metricIssue("shieldGenerator","unresolved",module)]):completeResult({module:module,budget:powered.value})}return incompleteResult([metricIssue("shieldGenerator","missing")])}function mapResistanceFields(read){return{kineticResistance:read("kineticResistance")??0,thermalResistance:read("thermalResistance")??0,explosiveResistance:read("explosiveResistance")??0,causticResistance:read("causticResistance")??0}}function resistancesOf(module,stats){return mapResistanceFields(field=>effectiveStat(module,field,stats))}function shieldInputFor(ship,modules,budget,statsFor){let generator=null;const boosters=[];let reinforcement=0;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(!(budget.available>0&&poweredStates(module,stats,budget).retracted))continue;if(!generator&&startsWithAny(module.Item,PREFIX.shieldGenerator)){const massRatio=modifierRatio(module,stats,"optMass");const strengthRatio=modifierRatio(module,stats,"optMultiplier");const optMass=effectiveStat(module,"optMass",stats);const minMass=relatedStat(module,stats,"minMass",massRatio);const maxMass=relatedStat(module,stats,"maxMass",Math.max(1,massRatio));const optMultiplier=effectiveStat(module,"optMultiplier",stats);const minMultiplier=relatedStat(module,stats,"minMultiplier",strengthRatio);const maxMultiplier=relatedStat(module,stats,"maxMultiplier",strengthRatio);generator={...optMass===void 0?{}:{optMass:optMass},...minMass===void 0?{}:{minMass:minMass},...maxMass===void 0?{}:{maxMass:maxMass},...optMultiplier===void 0?{}:{optMultiplier:optMultiplier},...minMultiplier===void 0?{}:{minMultiplier:minMultiplier},...maxMultiplier===void 0?{}:{maxMultiplier:maxMultiplier},...resistancesOf(module,stats)}}else if(startsWithAny(module.Item,PREFIX.shieldBooster)){boosters.push({shieldBoost:effectiveStat(module,"shieldBoost",stats)??0,...resistancesOf(module,stats)})}else if(startsWithAny(module.Item,PREFIX.shieldReinforcement)){reinforcement+=effectiveStat(module,"shieldAddition",stats)??0}}return{hullMass:ship.hullMass,baseShieldStrength:ship.baseShieldStrength,generator:generator,boosters:boosters,reinforcement:reinforcement}}function shieldInputResultFor(ship,modules,getBudget,statsFor){const fitted=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!fitted.complete)return fitted;return completeResult(shieldInputFor(ship,modules,fitted.value.budget,statsFor))}function shieldCapacitorInputResultFor(ship,modules,getBudget,systemsPips,statsFor){const fitted=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!fitted.complete)return fitted;const budget=fitted.value.budget;const capacitor=systemsCapacitorResultFor(modules,budget,statsFor);if(!capacitor.complete)return capacitor;const shields=shieldMetrics(shieldInputFor(ship,modules,budget,statsFor));return completeResult({strength:shields.strength,resistances:shields.resistances,systemsCapacity:capacitor.value.systemsCapacity,systemsRecharge:capacitor.value.systemsRecharge,systemsPips:systemsPips})}function systemsCapacitorResultFor(modules,budget,statsFor){let distributor=null;for(const module of modules){const stats2=statsFor(module);if(isPowerDistributor(module,stats2)&&isEnabled(module)&&budget.available>0&&poweredStates(module,stats2,budget).retracted)distributor=module}if(!distributor)return completeResult({systemsCapacity:0,systemsRecharge:0});const stats=statsFor(distributor);const systemsCapacity=effectiveStat(distributor,"systemsCapacity",stats);const systemsRecharge=effectiveStat(distributor,"systemsRecharge",stats);return systemsCapacity===void 0||systemsRecharge===void 0?incompleteResult([metricIssue("powerDistributor","unresolved",distributor)]):completeResult({systemsCapacity:systemsCapacity,systemsRecharge:systemsRecharge})}function thrusterCurveFor(module,stats){const effective=effectiveModule(module,stats);const minMass=effective?.minMass;const optMass=effective?.optMass;const maxMass=effective?.maxMass;const minMultiplier=effective?.minMultiplier;const optMultiplier=effective?.optMultiplier;const maxMultiplier=effective?.maxMultiplier;if(minMass===void 0||optMass===void 0||maxMass===void 0||minMultiplier===void 0||optMultiplier===void 0||maxMultiplier===void 0){return null}return{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:minMultiplier,optMultiplier:optMultiplier,maxMultiplier:maxMultiplier,...effective?.optSpeedMultiplier===void 0?{}:{speedCurve:{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:effective.minSpeedMultiplier??minMultiplier,optMultiplier:effective.optSpeedMultiplier,maxMultiplier:effective.maxSpeedMultiplier??maxMultiplier}},...effective?.optRotationMultiplier===void 0?{}:{rotationCurve:{minMass:minMass,optMass:optMass,maxMass:maxMass,minMultiplier:effective.minRotationMultiplier??minMultiplier,optMultiplier:effective.optRotationMultiplier,maxMultiplier:effective.maxRotationMultiplier??maxMultiplier}}}}function fittedThrusterParamsFor(modules,statsFor){for(const module of modules){const stats=statsFor(module);if(isThruster(module,stats))return thrusterCurveFor(module,stats)}return null}function mobilityInputResultFor(ship,modules,getBudget,mass,statsFor){let thrusters=null;for(const module of modules){const stats=statsFor(module);if(!isThruster(module,stats))continue;const powered=poweredMetricBudgetFor("thrusters",module,stats,modules,getBudget,statsFor);if(!powered.complete)return powered;thrusters=thrusterCurveFor(module,stats);if(!thrusters)return incompleteResult([metricIssue("thrusters","unresolved",module)]);break}if(!thrusters)return incompleteResult([metricIssue("thrusters","missing")]);return completeResult({minimumSpeed:ship.minimumSpeed,maximumSpeed:ship.maximumSpeed,boost:ship.boost,minPitch:ship.minPitch,pitch:ship.pitch,minRoll:ship.minRoll,roll:ship.roll,minYaw:ship.minYaw,yaw:ship.yaw,mass:typeof mass==="function"?mass():mass,thrusters:thrusters})}function shieldRecoveryInputResultFor(ship,modules,getBudget,systemsPips,statsFor){const generator=poweredShieldGeneratorResultFor(modules,getBudget,statsFor);if(!generator.complete)return generator;const{module:generatorModule,budget:budget}=generator.value;const generatorStats=statsFor(generatorModule);const distributorDraw=effectiveStat(generatorModule,"distributorDraw",generatorStats);const regenRate=effectiveStat(generatorModule,"shieldRegenRate",generatorStats);const brokenRegenRate=effectiveStat(generatorModule,"shieldBrokenRegenRate",generatorStats);if(distributorDraw===void 0||regenRate===void 0||brokenRegenRate===void 0){return incompleteResult([metricIssue("shieldGenerator","unresolved",generatorModule)])}const capacitor=systemsCapacitorResultFor(modules,budget,statsFor);if(!capacitor.complete)return capacitor;const strength=shieldMetrics(shieldInputFor(ship,modules,budget,statsFor)).strength;return completeResult({strength:strength,regenRate:regenRate,brokenRegenRate:brokenRegenRate,distributorDraw:distributorDraw,systemsCapacity:capacitor.value.systemsCapacity,systemsRecharge:capacitor.value.systemsRecharge,systemsPips:systemsPips})}function weaponsCapacitorInputFor(modules,weaponsPips,budget,statsFor){let weaponsCapacity=0;let weaponsRecharge=0;let sustainedEnergyPerSecond=0;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(!poweredStates(module,stats,budget).deployed)continue;if(isPowerDistributor(module,stats)){weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats)??0;weaponsRecharge=effectiveStat(module,"weaponsRecharge",stats)??0}const weapon=weaponStatsFor(module,stats);if(weapon){sustainedEnergyPerSecond+=weaponMetrics(weapon).sustainedEnergyPerSecond}}return{weaponsCapacity:weaponsCapacity,weaponsRecharge:weaponsRecharge,sustainedEnergyPerSecond:sustainedEnergyPerSecond,weaponsPips:weaponsPips}}function distributorInputResultFor(modules,pips,budget,statsFor){let disabled=null;for(const module of modules){const stats=statsFor(module);if(!isPowerDistributor(module,stats))continue;if(!isEnabled(module)){disabled??=module;continue}if(!poweredStates(module,stats,budget).retracted){return incompleteResult([metricIssue("powerDistributor","shed",module)])}const systemsCapacity=effectiveStat(module,"systemsCapacity",stats);const systemsRecharge=effectiveStat(module,"systemsRecharge",stats);const enginesCapacity=effectiveStat(module,"enginesCapacity",stats);const enginesRecharge=effectiveStat(module,"enginesRecharge",stats);const weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats);const weaponsRecharge=effectiveStat(module,"weaponsRecharge",stats);if(systemsCapacity===void 0||systemsRecharge===void 0||enginesCapacity===void 0||enginesRecharge===void 0||weaponsCapacity===void 0||weaponsRecharge===void 0){return incompleteResult([metricIssue("powerDistributor","unresolved",module)])}return completeResult({systemsCapacity:systemsCapacity,systemsRecharge:systemsRecharge,enginesCapacity:enginesCapacity,enginesRecharge:enginesRecharge,weaponsCapacity:weaponsCapacity,weaponsRecharge:weaponsRecharge,...pips})}return incompleteResult([disabled===null?metricIssue("powerDistributor","missing"):metricIssue("powerDistributor","disabled",disabled)])}function heatInputResultFor(ship,modules,budget,statsFor){let plant=null;let heatEfficiency;let thrusterHeatRate=0;let deployedThrusterHeatRate=0;let fsdHeatRate=0;let weaponsCapacity=0;const weapons=[];for(const module of modules){const stats=statsFor(module);if(isPowerPlant(module,stats)){if(!isEnabled(module)){return incompleteResult([metricIssue("powerCapacity","disabled",module)])}plant=module;heatEfficiency=effectiveStat(module,"heatEfficiency",stats);continue}if(!isEnabled(module))continue;const running=poweredStates(module,stats,budget);if(isThruster(module,stats)){const heat=effectiveStat(module,"engineHeatRate",stats)??0;if(running.retracted)thrusterHeatRate+=heat;if(running.deployed)deployedThrusterHeatRate+=heat}else if(isFrameShiftDrive(module,stats)){if(running.retracted)fsdHeatRate+=effectiveStat(module,"fsdHeatRate",stats)??0}else if(isPowerDistributor(module,stats)){if(running.deployed){weaponsCapacity=effectiveStat(module,"weaponsCapacity",stats)??0}}const weapon=weaponStatsFor(module,stats);if(!running.deployed)continue;if(!weapon)continue;weapons.push({heatPerSecond:weaponMetrics(weapon).sustainedHeatPerSecond,distributorDraw:weapon.distributorDraw??0})}if(heatEfficiency===void 0){return incompleteResult([plant===null?metricIssue("powerCapacity","missing"):metricIssue("heatEfficiency","unresolved",plant)])}return completeResult({heatCapacity:ship.heatCapacity,heatDissipation:ship.heatDissipation,heatEfficiency:heatEfficiency,retractedPowerDraw:poweredDraw(budget,"retracted"),deployedPowerDraw:poweredDraw(budget,"deployed"),thrusterHeatRate:thrusterHeatRate,deployedThrusterHeatRate:deployedThrusterHeatRate,fsdHeatRate:fsdHeatRate,weaponsCapacity:weaponsCapacity,weapons:weapons})}function poweredDraw(budget,state){const powered=state==="retracted"?"poweredRetracted":"poweredDeployed";return budget.bands.reduce((total,band)=>band[powered]?total+band[state]:total,0)}function poweredStates(module,stats,budget){const draw=effectiveStat(module,"powerDraw",stats);if(draw===void 0||draw===0)return{retracted:true,deployed:true};const band=budget.bands[Math.min(budget.bands.length,Math.max(1,priorityOf(module)))-1];if(!band)return{retracted:true,deployed:true};return{retracted:band.poweredRetracted,deployed:band.poweredDeployed}}function cellBankInputsFor(modules,budget,statsFor){const banks=[];for(const module of modules){if(!startsWithAny(module.Item,PREFIX.shieldCellBank))continue;const stats=statsFor(module);const effective=effectiveModule(module,stats);const ammunition=effective?ammunitionCapacity(effective):null;banks.push({slot:module.Slot,symbol:module.Item,reinforcementRate:effective?.shieldBankReinforcement??0,cells:ammunition?.total??0,spinUp:effective?.shieldBankSpinUp??0,duration:effective?.shieldBankDuration??0,heat:effective?.shieldBankHeat??0,powered:budget.available>0&&isEnabled(module)&&poweredStates(module,stats,budget).deployed})}return banks}function stockBulkhead(ship){const variants=getBulkheadsForShip(ship.name,CORE_MODULES);return variants.find(v=>v.symbol.toLowerCase().endsWith("_default"))??variants.find(v=>v.mass===0)??variants[0]??null}function armourInputFor(ship,modules,statsFor){const reinforcements=[];const moduleReinforcements=[];let bulkhead=null;for(const module of modules){if(!isEnabled(module))continue;const stats=statsFor(module);if(module.Slot.toLowerCase()==="armour"){bulkhead={hullBoost:effectiveStat(module,"hullBoost",stats)??0,...resistancesOf(module,stats)}}else if(startsWithAny(module.Item,PREFIX.hullReinforcement)){const boost=getLoadoutModifier(module,"DefenceModifierHealthMultiplier");reinforcements.push({hullReinforcement:effectiveStat(module,"hullReinforcement",stats)??0,hullBoost:boost===null?0:boost/scaleForLabel("DefenceModifierHealthMultiplier"),...resistancesOf(module,stats)})}else if(startsWithAny(module.Item,PREFIX.moduleReinforcement)){moduleReinforcements.push({moduleProtection:effectiveStat(module,"moduleProtection",stats)??0,integrity:effectiveStat(module,"integrity",stats)??0})}}if(!bulkhead){const stock=stockBulkhead(ship);bulkhead=stock?{hullBoost:stock.hullBoost??0,...mapResistanceFields(field=>stock[field])}:null}return{baseArmour:ship.baseArmour,bulkhead:bulkhead,reinforcements:reinforcements,moduleReinforcements:moduleReinforcements}}var WEAPON_FIELDS=["damage","roundsPerShot","rateOfFire","burstInterval","burstRounds","burstRateOfFire","chargeTime","clipSize","ammoMaximum","reloadTime","distributorDraw","thermalLoad","powerDraw","maximumRange","falloffRange","armourPiercing"];function weaponStatsFor(module,stats){if(!stats||stats.category!=="hardpoint")return null;const weapon={};for(const field of WEAPON_FIELDS){const value=effectiveStat(module,field,stats);if(value!==void 0)weapon[field]=value}normalizeEffectiveWeapon(module,weapon);const damageDistribution=effectiveDamageDistribution(module,stats);if(damageDistribution)weapon.damageDistribution=damageDistribution;if(stats.damageComponents&&!convertsDamage(module)){weapon.damageComponents=scaleDamageComponents(stats.damageComponents,stats.damage,typeof weapon.damage==="number"?weapon.damage:void 0)}if(stats.projectileRange)weapon.projectileRange={...stats.projectileRange};return weapon}function burstAdjustedRateOfFire(module,weapon){if(getLoadoutModifier(module,"RateOfFire")!==null)return void 0;const touched=["BurstSize","BurstRateOfFire","BurstInterval"].some(label=>getLoadoutModifier(module,label)!==null);if(!touched)return void 0;return combinedRateOfFire(weapon)}function normalizeEffectiveWeapon(module,weapon){const rate=burstAdjustedRateOfFire(module,weapon);if(rate!==void 0)weapon.rateOfFire=rate;const statedDamagePerSecond=getLoadoutModifier(module,"DamagePerSecond");const firingFactor=Number(weapon.roundsPerShot??1)*Number(weapon.rateOfFire??1);if(statedDamagePerSecond!==null&&firingFactor>0){weapon.damage=statedDamagePerSecond/firingFactor}const{maximumRange:maximumRange,falloffRange:falloffRange}=weapon;if(maximumRange!==void 0&&falloffRange!==void 0&&falloffRange>maximumRange){weapon.falloffRange=maximumRange}}var INTERNALS=/* @__PURE__ */new WeakMap;function publishLoadoutInternals(build,internals){INTERNALS.set(build,internals)}function loadoutInternals(build,label){const internals=typeof build==="object"&&build!==null?INTERNALS.get(build):void 0;if(internals===void 0){throw new TypeError(`${label} must be a ShipLoadout, received ${describeValue(build)}`)}return internals}export{armourInputFor,cellBankInputsFor,completeResult,distributorInputResultFor,effectiveModule,fittedThrusterParamsFor,heatInputResultFor,incompleteResult,loadoutInternals,mobilityInputResultFor,moduleFitError,moduleFitProblem,normalizeLoadoutEvent,powerAvailable,powerConsumerFor,preEngineeredVariantFor,publishLoadoutInternals,shieldCapacitorInputResultFor,shieldInputResultFor,shieldRecoveryInputResultFor,weaponStatsFor,weaponsCapacitorInputFor};//# sourceMappingURL=chunk-6IWKFKQF.js.map
|