@shipload/sdk 1.0.0-next.3 → 1.0.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipload/sdk",
3
3
  "description": "SDKs for Shipload",
4
- "version": "1.0.0-next.3",
4
+ "version": "1.0.0-next.4",
5
5
  "homepage": "https://github.com/shipload/toolkit/tree/master/packages/sdk",
6
6
  "repository": {
7
7
  "type": "git",
@@ -8,7 +8,6 @@ export interface StatMapping {
8
8
  stat: string
9
9
  capability: string
10
10
  attribute: string
11
- rationale: string
12
11
  }
13
12
 
14
13
  export const capabilityNames: string[] = [
@@ -27,6 +26,11 @@ export const capabilityNames: string[] = [
27
26
  export const capabilityAttributes: CapabilityAttribute[] = [
28
27
  {capability: 'Hull', attribute: 'mass', description: 'Total mass of the hull'},
29
28
  {capability: 'Storage', attribute: 'capacity', description: 'Maximum mass that can be stored'},
29
+ {
30
+ capability: 'Storage',
31
+ attribute: 'bonus',
32
+ description: 'Capacity bonus added by an installed Storage module',
33
+ },
30
34
  {capability: 'Movement', attribute: 'thrust', description: 'Propulsion force'},
31
35
  {capability: 'Movement', attribute: 'drain', description: 'Energy consumed per movement'},
32
36
  {capability: 'Energy', attribute: 'capacity', description: 'Maximum energy storage'},
@@ -65,323 +69,6 @@ export const capabilityAttributes: CapabilityAttribute[] = [
65
69
  },
66
70
  ]
67
71
 
68
- export const statMappings: StatMapping[] = [
69
- {
70
- stat: 'Strength',
71
- capability: 'Gathering',
72
- attribute: 'yield',
73
- rationale: 'Raw mechanical force drives faster gathering',
74
- },
75
- {
76
- stat: 'Strength',
77
- capability: 'Storage',
78
- attribute: 'capacity',
79
- rationale: 'Stronger walls hold more capacity per mass',
80
- },
81
- {
82
- stat: 'Strength',
83
- capability: 'Launch',
84
- attribute: 'capacity',
85
- rationale: 'Stronger housing handles larger launch loads',
86
- },
87
- {
88
- stat: 'Tolerance',
89
- capability: 'Movement',
90
- attribute: 'thrust',
91
- rationale: 'Engine components that tolerate more can push harder',
92
- },
93
- {
94
- stat: 'Tolerance',
95
- capability: 'Energy',
96
- attribute: 'recharge',
97
- rationale: 'Generator housing withstands stress for faster recharge',
98
- },
99
- {
100
- stat: 'Tolerance',
101
- capability: 'Gathering',
102
- attribute: 'depth',
103
- rationale: 'Housing withstands pressure/heat at extreme depths',
104
- },
105
- {
106
- stat: 'Tolerance',
107
- capability: 'Warp',
108
- attribute: 'range',
109
- rationale: 'Warp drive housing withstands extreme forces',
110
- },
111
- {
112
- stat: 'Density',
113
- capability: 'Hull',
114
- attribute: 'mass',
115
- rationale: 'Lighter metal = lighter hull',
116
- },
117
- {
118
- stat: 'Density',
119
- capability: 'Loader',
120
- attribute: 'mass',
121
- rationale: 'Lighter metal = lighter loader units',
122
- },
123
- {
124
- stat: 'Density',
125
- capability: 'Movement',
126
- attribute: 'drain',
127
- rationale: 'Lighter components require less energy to move',
128
- },
129
- {
130
- stat: 'Conductivity',
131
- capability: 'Movement',
132
- attribute: 'drain',
133
- rationale: 'Efficient energy transfer reduces movement energy cost',
134
- },
135
- {
136
- stat: 'Conductivity',
137
- capability: 'Gathering',
138
- attribute: 'drain',
139
- rationale: 'Efficient energy transfer reduces gathering energy cost',
140
- },
141
- {
142
- stat: 'Conductivity',
143
- capability: 'Crafter',
144
- attribute: 'drain',
145
- rationale: 'Efficient energy transfer reduces crafting energy cost',
146
- },
147
- {
148
- stat: 'Conductivity',
149
- capability: 'Energy',
150
- attribute: 'recharge',
151
- rationale: 'Better conductivity speeds energy flow during recharge',
152
- },
153
- {
154
- stat: 'Ductility',
155
- capability: 'Crafter',
156
- attribute: 'quality',
157
- rationale: 'Precise shaping enables tighter crafting tolerances',
158
- },
159
- {
160
- stat: 'Ductility',
161
- capability: 'Gathering',
162
- attribute: 'yield',
163
- rationale: 'Precisely shaped conduit components gather faster',
164
- },
165
- {
166
- stat: 'Ductility',
167
- capability: 'Storage',
168
- attribute: 'capacity',
169
- rationale: 'Precision-formed container walls maximize volume',
170
- },
171
- {
172
- stat: 'Ductility',
173
- capability: 'Loader',
174
- attribute: 'mass',
175
- rationale: 'Precision-formed precious metal reduces loader unit mass',
176
- },
177
- {
178
- stat: 'Reflectivity',
179
- capability: 'Gathering',
180
- attribute: 'depth',
181
- rationale: 'Reflective heat shielding protects equipment at depth',
182
- },
183
- {
184
- stat: 'Reflectivity',
185
- capability: 'Launch',
186
- attribute: 'range',
187
- rationale: 'Reflective surfaces focus electromagnetic launch energy',
188
- },
189
- {
190
- stat: 'Volatility',
191
- capability: 'Gathering',
192
- attribute: 'yield',
193
- rationale: 'Energy release powers faster gathering',
194
- },
195
- {
196
- stat: 'Volatility',
197
- capability: 'Movement',
198
- attribute: 'thrust',
199
- rationale: 'Energy release drives propulsion force',
200
- },
201
- {
202
- stat: 'Volatility',
203
- capability: 'Loader',
204
- attribute: 'thrust',
205
- rationale: 'Energy release powers loader motors',
206
- },
207
- {
208
- stat: 'Volatility',
209
- capability: 'Launch',
210
- attribute: 'capacity',
211
- rationale: 'Energy release enables launching heavier payloads',
212
- },
213
- {
214
- stat: 'Reactivity',
215
- capability: 'Crafter',
216
- attribute: 'speed',
217
- rationale: 'Reactive gases accelerate chemical/thermal processing',
218
- },
219
- {
220
- stat: 'Reactivity',
221
- capability: 'Gathering',
222
- attribute: 'speed',
223
- rationale: 'Reactive gases manage heat/friction during gathering',
224
- },
225
- {
226
- stat: 'Reactivity',
227
- capability: 'Launch',
228
- attribute: 'drain',
229
- rationale: 'Reactive gas medium reduces electromagnetic resistance',
230
- },
231
- {
232
- stat: 'Thermal',
233
- capability: 'Crafter',
234
- attribute: 'quality',
235
- rationale: 'Precise thermal control during fabrication',
236
- },
237
- {
238
- stat: 'Thermal',
239
- capability: 'Gathering',
240
- attribute: 'drain',
241
- rationale: 'Thermal management reduces energy waste during gathering',
242
- },
243
- {
244
- stat: 'Thermal',
245
- capability: 'Energy',
246
- attribute: 'capacity',
247
- rationale: 'Thermal management enables denser energy storage',
248
- },
249
- {
250
- stat: 'Resonance',
251
- capability: 'Energy',
252
- attribute: 'capacity',
253
- rationale: 'Resonating crystals store energy in fields',
254
- },
255
- {
256
- stat: 'Resonance',
257
- capability: 'Warp',
258
- attribute: 'range',
259
- rationale: 'Resonant crystals amplify warp field projection',
260
- },
261
- {
262
- stat: 'Resonance',
263
- capability: 'Launch',
264
- attribute: 'range',
265
- rationale: 'Resonant crystals focus electromagnetic launch field',
266
- },
267
- {
268
- stat: 'Resonance',
269
- capability: 'Launch',
270
- attribute: 'capacity',
271
- rationale: 'Stronger resonant field launches heavier payloads',
272
- },
273
- {
274
- stat: 'Hardness',
275
- capability: 'Crafter',
276
- attribute: 'speed',
277
- rationale: 'Hard tooling surfaces cut and shape materials faster',
278
- },
279
- {
280
- stat: 'Hardness',
281
- capability: 'Launch',
282
- attribute: 'drain',
283
- rationale: 'Hard rail surfaces reduce friction, less energy wasted',
284
- },
285
- {
286
- stat: 'Clarity',
287
- capability: 'Energy',
288
- attribute: 'recharge',
289
- rationale: 'Flawless crystals enable smoother energy flow during recharge',
290
- },
291
- {
292
- stat: 'Clarity',
293
- capability: 'Crafter',
294
- attribute: 'quality',
295
- rationale: 'Precision optics for calibration during fabrication',
296
- },
297
- {
298
- stat: 'Clarity',
299
- capability: 'Crafter',
300
- attribute: 'drain',
301
- rationale: 'Precision computing optimizes energy routing in factory',
302
- },
303
- {
304
- stat: 'Plasticity',
305
- capability: 'Crafter',
306
- attribute: 'speed',
307
- rationale: 'Easily reshaped materials speed up processing',
308
- },
309
- {
310
- stat: 'Plasticity',
311
- capability: 'Movement',
312
- attribute: 'thrust',
313
- rationale: 'Flexible polymer seals reduce friction in propulsion',
314
- },
315
- {
316
- stat: 'Plasticity',
317
- capability: 'Loader',
318
- attribute: 'thrust',
319
- rationale: 'Flexible joints improve loader force transfer',
320
- },
321
- {
322
- stat: 'Insulation',
323
- capability: 'Movement',
324
- attribute: 'drain',
325
- rationale: 'Better insulation reduces energy loss during movement',
326
- },
327
- {
328
- stat: 'Insulation',
329
- capability: 'Gathering',
330
- attribute: 'drain',
331
- rationale: 'Better insulation reduces energy loss during gathering',
332
- },
333
- {
334
- stat: 'Insulation',
335
- capability: 'Crafter',
336
- attribute: 'drain',
337
- rationale: 'Better insulation reduces energy loss during crafting',
338
- },
339
- {
340
- stat: 'Insulation',
341
- capability: 'Launch',
342
- attribute: 'drain',
343
- rationale: 'Better insulation reduces energy loss during launch',
344
- },
345
- {
346
- stat: 'Purity',
347
- capability: 'Storage',
348
- attribute: 'capacity',
349
- rationale: 'Purer composites make better containers',
350
- },
351
- {
352
- stat: 'Purity',
353
- capability: 'Gathering',
354
- attribute: 'speed',
355
- rationale: 'Purer bio-lubricants reduce friction during gathering',
356
- },
357
- {
358
- stat: 'Purity',
359
- capability: 'Energy',
360
- attribute: 'capacity',
361
- rationale: 'Purer organic electrolytes store more charge',
362
- },
363
- {
364
- stat: 'Resonance',
365
- capability: 'Hauler',
366
- attribute: 'capacity',
367
- rationale:
368
- 'Resonant field strength determines how many targets the haul beam can lock onto simultaneously.',
369
- },
370
- {
371
- stat: 'Conductivity',
372
- capability: 'Hauler',
373
- attribute: 'efficiency',
374
- rationale: 'Energy-transfer efficiency reduces the thrust penalty from each hauled target.',
375
- },
376
- {
377
- stat: 'Clarity',
378
- capability: 'Hauler',
379
- attribute: 'drain',
380
- rationale:
381
- 'Clarity-focused energy routing reduces per-target drain during haul-beam operation.',
382
- },
383
- ]
384
-
385
72
  const invertedAttributes = new Set(['drain', 'mass'])
386
73
 
387
74
  export function isInvertedAttribute(attribute: string): boolean {
@@ -394,15 +81,3 @@ export function getCapabilityAttributes(capability?: string): CapabilityAttribut
394
81
  }
395
82
  return capabilityAttributes
396
83
  }
397
-
398
- export function getStatMappings(): StatMapping[] {
399
- return statMappings
400
- }
401
-
402
- export function getStatMappingsForStat(stat: string): StatMapping[] {
403
- return statMappings.filter((m) => m.stat === stat)
404
- }
405
-
406
- export function getStatMappingsForCapability(capability: string): StatMapping[] {
407
- return statMappings.filter((m) => m.capability === capability)
408
- }
@@ -0,0 +1,68 @@
1
+ export interface SlotConsumer {
2
+ capability: string
3
+ attribute: string
4
+ }
5
+
6
+ export type SlotConsumerKind =
7
+ | 'engine'
8
+ | 'generator'
9
+ | 'gatherer'
10
+ | 'loader'
11
+ | 'crafter'
12
+ | 'storage'
13
+ | 'hauler'
14
+ | 'warp'
15
+ | 'ship-t1'
16
+ | 'container-t1'
17
+ | 'warehouse-t1'
18
+ | 'container-t2'
19
+
20
+ const ENTITY_HULL_SLOTS: Record<number, SlotConsumer> = {
21
+ 0: {capability: 'Storage', attribute: 'capacity'},
22
+ 1: {capability: 'Hull', attribute: 'mass'},
23
+ 2: {capability: 'Storage', attribute: 'capacity'},
24
+ 3: {capability: 'Storage', attribute: 'capacity'},
25
+ }
26
+
27
+ export const SLOT_FORMULAS: Record<SlotConsumerKind, Record<number, SlotConsumer>> = {
28
+ engine: {
29
+ 0: {capability: 'Movement', attribute: 'thrust'},
30
+ 1: {capability: 'Movement', attribute: 'drain'},
31
+ },
32
+ generator: {
33
+ 0: {capability: 'Energy', attribute: 'capacity'},
34
+ 1: {capability: 'Energy', attribute: 'recharge'},
35
+ },
36
+ gatherer: {
37
+ 0: {capability: 'Gathering', attribute: 'yield'},
38
+ 1: {capability: 'Gathering', attribute: 'depth'},
39
+ 3: {capability: 'Gathering', attribute: 'drain'},
40
+ 4: {capability: 'Gathering', attribute: 'speed'},
41
+ },
42
+ loader: {
43
+ 0: {capability: 'Loader', attribute: 'mass'},
44
+ 1: {capability: 'Loader', attribute: 'thrust'},
45
+ },
46
+ crafter: {
47
+ 0: {capability: 'Crafter', attribute: 'speed'},
48
+ 1: {capability: 'Crafter', attribute: 'drain'},
49
+ },
50
+ storage: {
51
+ 0: {capability: 'Storage', attribute: 'bonus'},
52
+ 1: {capability: 'Storage', attribute: 'bonus'},
53
+ 2: {capability: 'Storage', attribute: 'bonus'},
54
+ 3: {capability: 'Storage', attribute: 'bonus'},
55
+ },
56
+ hauler: {
57
+ 0: {capability: 'Hauler', attribute: 'capacity'},
58
+ 1: {capability: 'Hauler', attribute: 'efficiency'},
59
+ 2: {capability: 'Hauler', attribute: 'drain'},
60
+ },
61
+ warp: {
62
+ 0: {capability: 'Warp', attribute: 'range'},
63
+ },
64
+ 'ship-t1': ENTITY_HULL_SLOTS,
65
+ 'container-t1': ENTITY_HULL_SLOTS,
66
+ 'warehouse-t1': ENTITY_HULL_SLOTS,
67
+ 'container-t2': ENTITY_HULL_SLOTS,
68
+ }
@@ -393,12 +393,7 @@
393
393
  ]
394
394
  },
395
395
  {
396
- "sources": [
397
- {
398
- "inputIndex": 1,
399
- "statIndex": 1
400
- }
401
- ]
396
+ "sources": []
402
397
  },
403
398
  {
404
399
  "sources": [
@@ -436,8 +431,8 @@
436
431
  {
437
432
  "sources": [
438
433
  {
439
- "inputIndex": 0,
440
- "statIndex": 0
434
+ "inputIndex": 1,
435
+ "statIndex": 1
441
436
  }
442
437
  ]
443
438
  },
@@ -552,7 +547,7 @@
552
547
  "sources": [
553
548
  {
554
549
  "inputIndex": 0,
555
- "statIndex": 0
550
+ "statIndex": 1
556
551
  }
557
552
  ]
558
553
  },
@@ -568,17 +563,12 @@
568
563
  "sources": [
569
564
  {
570
565
  "inputIndex": 0,
571
- "statIndex": 1
566
+ "statIndex": 0
572
567
  }
573
568
  ]
574
569
  },
575
570
  {
576
- "sources": [
577
- {
578
- "inputIndex": 1,
579
- "statIndex": 1
580
- }
581
- ]
571
+ "sources": []
582
572
  }
583
573
  ],
584
574
  "blendWeights": []
@@ -0,0 +1,120 @@
1
+ import {SLOT_FORMULAS, type SlotConsumerKind} from '../data/capability-formulas'
2
+ import {getStatDefinitions, type StatDefinition} from './stats'
3
+ import {
4
+ getRecipe,
5
+ type Recipe,
6
+ type RecipeInput,
7
+ type RecipeInputCategory,
8
+ } from '../data/recipes-runtime'
9
+ import {
10
+ ITEM_ENGINE_T1,
11
+ ITEM_GENERATOR_T1,
12
+ ITEM_GATHERER_T1,
13
+ ITEM_LOADER_T1,
14
+ ITEM_CRAFTER_T1,
15
+ ITEM_STORAGE_T1,
16
+ ITEM_HAULER_T1,
17
+ ITEM_WARP_T1,
18
+ ITEM_SHIP_T1_PACKED,
19
+ ITEM_CONTAINER_T1_PACKED,
20
+ ITEM_WAREHOUSE_T1_PACKED,
21
+ ITEM_CONTAINER_T2_PACKED,
22
+ } from '../data/item-ids'
23
+ import type {StatMapping} from '../data/capabilities'
24
+
25
+ export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
26
+ engine: ITEM_ENGINE_T1,
27
+ generator: ITEM_GENERATOR_T1,
28
+ gatherer: ITEM_GATHERER_T1,
29
+ loader: ITEM_LOADER_T1,
30
+ crafter: ITEM_CRAFTER_T1,
31
+ storage: ITEM_STORAGE_T1,
32
+ hauler: ITEM_HAULER_T1,
33
+ warp: ITEM_WARP_T1,
34
+ 'ship-t1': ITEM_SHIP_T1_PACKED,
35
+ 'container-t1': ITEM_CONTAINER_T1_PACKED,
36
+ 'warehouse-t1': ITEM_WAREHOUSE_T1_PACKED,
37
+ 'container-t2': ITEM_CONTAINER_T2_PACKED,
38
+ }
39
+
40
+ function isCategoryInput(input: RecipeInput): input is RecipeInputCategory {
41
+ return 'category' in input
42
+ }
43
+
44
+ /**
45
+ * Walk a recipe's slot source down to the raw category stat that ultimately
46
+ * lands in that slot. Returns the StatDefinition or undefined if the trace
47
+ * dead-ends (unknown sub-component, missing slot, etc.).
48
+ *
49
+ * Multi-source sub-slots collapse to `sources[0]`; top-level multi-source slots
50
+ * are expanded by the caller (`deriveStatMappings`).
51
+ */
52
+ function traceToRawCategoryStat(
53
+ recipe: Recipe,
54
+ source: {inputIndex: number; statIndex: number},
55
+ visited: Set<number> = new Set()
56
+ ): StatDefinition | undefined {
57
+ const input = recipe.inputs[source.inputIndex]
58
+ if (!input) return undefined
59
+ if (isCategoryInput(input)) {
60
+ const defs = getStatDefinitions(input.category)
61
+ return defs[source.statIndex]
62
+ }
63
+ if (visited.has(input.itemId)) return undefined
64
+ const subRecipe = getRecipe(input.itemId)
65
+ if (!subRecipe) return undefined
66
+ const subSlot = subRecipe.statSlots[source.statIndex]
67
+ if (!subSlot) return undefined
68
+ const subSource = subSlot.sources[0]
69
+ if (!subSource) return undefined
70
+ const nextVisited = new Set(visited)
71
+ nextVisited.add(input.itemId)
72
+ return traceToRawCategoryStat(subRecipe, subSource, nextVisited)
73
+ }
74
+
75
+ let cached: StatMapping[] | undefined
76
+
77
+ export function deriveStatMappings(): StatMapping[] {
78
+ if (cached) return cached
79
+ const out: StatMapping[] = []
80
+ const seen = new Set<string>()
81
+ for (const [kind, slots] of Object.entries(SLOT_FORMULAS) as [
82
+ SlotConsumerKind,
83
+ Record<number, {capability: string; attribute: string}>,
84
+ ][]) {
85
+ const itemId = KIND_TO_ITEM_ID[kind]
86
+ const recipe = getRecipe(itemId)
87
+ if (!recipe) continue
88
+ for (const [slotIdxStr, consumer] of Object.entries(slots)) {
89
+ const slotIdx = Number(slotIdxStr)
90
+ const slot = recipe.statSlots[slotIdx]
91
+ if (!slot) continue
92
+ for (const source of slot.sources) {
93
+ const stat = traceToRawCategoryStat(recipe, source)
94
+ if (!stat) continue
95
+ const key = `${stat.label}|${consumer.capability}|${consumer.attribute}`
96
+ if (seen.has(key)) continue
97
+ seen.add(key)
98
+ out.push({
99
+ stat: stat.label,
100
+ capability: consumer.capability,
101
+ attribute: consumer.attribute,
102
+ })
103
+ }
104
+ }
105
+ }
106
+ cached = out
107
+ return out
108
+ }
109
+
110
+ export function getStatMappings(): StatMapping[] {
111
+ return deriveStatMappings()
112
+ }
113
+
114
+ export function getStatMappingsForStat(stat: string): StatMapping[] {
115
+ return deriveStatMappings().filter((m) => m.stat === stat)
116
+ }
117
+
118
+ export function getStatMappingsForCapability(capability: string): StatMapping[] {
119
+ return deriveStatMappings().filter((m) => m.capability === capability)
120
+ }
@@ -75,10 +75,10 @@ export function computeContainerCapabilities(stats: Record<string, number>): {
75
75
  hullmass: number
76
76
  capacity: number
77
77
  } {
78
- const density = stats['density'] ?? 500
79
- const strength = stats['strength'] ?? 500
80
- const hardness = stats['hardness'] ?? 500
81
- const saturation = stats['saturation'] ?? 500
78
+ const density = stats.density
79
+ const strength = stats.strength
80
+ const hardness = stats.hardness
81
+ const saturation = stats.saturation
82
82
 
83
83
  const hullmass = 25000 + 75 * density
84
84
 
@@ -93,10 +93,10 @@ export function computeContainerT2Capabilities(stats: Record<string, number>): {
93
93
  hullmass: number
94
94
  capacity: number
95
95
  } {
96
- const strength = stats['strength'] ?? 0
97
- const density = stats['density'] ?? 0
98
- const hardness = stats['hardness'] ?? 0
99
- const saturation = stats['saturation'] ?? 0
96
+ const strength = stats.strength
97
+ const density = stats.density
98
+ const hardness = stats.hardness
99
+ const saturation = stats.saturation
100
100
 
101
101
  const hullmass = 20000 + 50 * density
102
102