@shipload/sdk 1.0.0-next.17 → 1.0.0-next.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/shipload.d.ts +136 -16
  2. package/lib/shipload.js +505 -149
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +494 -151
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +18 -6
  7. package/lib/testing.js +78 -40
  8. package/lib/testing.js.map +1 -1
  9. package/lib/testing.m.js +79 -41
  10. package/lib/testing.m.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/capabilities/gathering.ts +2 -1
  13. package/src/contracts/server.ts +52 -15
  14. package/src/data/capability-formulas.ts +1 -1
  15. package/src/data/items.json +24 -24
  16. package/src/data/kind-registry.json +7 -0
  17. package/src/data/kind-registry.ts +14 -1
  18. package/src/data/recipes-runtime.ts +5 -0
  19. package/src/data/recipes.json +64 -54
  20. package/src/derivation/build-methods.ts +45 -0
  21. package/src/derivation/capabilities.ts +11 -5
  22. package/src/derivation/index.ts +7 -2
  23. package/src/derivation/reserve-regen.ts +34 -0
  24. package/src/derivation/resources.ts +9 -1
  25. package/src/derivation/stratum.ts +10 -10
  26. package/src/derivation/tiers.ts +28 -7
  27. package/src/entities/makers.ts +2 -0
  28. package/src/index-module.ts +28 -2
  29. package/src/managers/actions.ts +19 -0
  30. package/src/managers/construction-types.ts +47 -0
  31. package/src/managers/construction.ts +147 -0
  32. package/src/managers/index.ts +11 -0
  33. package/src/managers/plot.ts +123 -0
  34. package/src/nft/buildImmutableData.ts +9 -9
  35. package/src/nft/description.ts +6 -6
  36. package/src/resolution/describe-module.ts +2 -4
  37. package/src/subscriptions/types.ts +1 -0
  38. package/src/types.ts +2 -0
@@ -1,12 +1,12 @@
1
1
  [
2
2
  {
3
3
  "outputItemId": 10001,
4
- "outputMass": 50000,
4
+ "outputMass": 420000,
5
5
  "inputs": [
6
6
  {
7
7
  "category": "ore",
8
8
  "tier": 1,
9
- "quantity": 15
9
+ "quantity": 20
10
10
  }
11
11
  ],
12
12
  "statSlots": [
@@ -31,12 +31,12 @@
31
31
  },
32
32
  {
33
33
  "outputItemId": 10002,
34
- "outputMass": 30000,
34
+ "outputMass": 600000,
35
35
  "inputs": [
36
36
  {
37
37
  "category": "regolith",
38
38
  "tier": 1,
39
- "quantity": 10
39
+ "quantity": 30
40
40
  },
41
41
  {
42
42
  "category": "biomass",
@@ -66,12 +66,12 @@
66
66
  },
67
67
  {
68
68
  "outputItemId": 10003,
69
- "outputMass": 50000,
69
+ "outputMass": 390000,
70
70
  "inputs": [
71
71
  {
72
72
  "category": "gas",
73
73
  "tier": 1,
74
- "quantity": 32
74
+ "quantity": 65
75
75
  }
76
76
  ],
77
77
  "statSlots": [
@@ -96,12 +96,17 @@
96
96
  },
97
97
  {
98
98
  "outputItemId": 10004,
99
- "outputMass": 30000,
99
+ "outputMass": 600000,
100
100
  "inputs": [
101
101
  {
102
102
  "category": "crystal",
103
103
  "tier": 1,
104
- "quantity": 20
104
+ "quantity": 25
105
+ },
106
+ {
107
+ "category": "biomass",
108
+ "tier": 1,
109
+ "quantity": 15
105
110
  }
106
111
  ],
107
112
  "statSlots": [
@@ -126,12 +131,17 @@
126
131
  },
127
132
  {
128
133
  "outputItemId": 10005,
129
- "outputMass": 50000,
134
+ "outputMass": 600000,
130
135
  "inputs": [
131
136
  {
132
137
  "category": "ore",
133
138
  "tier": 1,
134
- "quantity": 15
139
+ "quantity": 20
140
+ },
141
+ {
142
+ "category": "gas",
143
+ "tier": 1,
144
+ "quantity": 30
135
145
  }
136
146
  ],
137
147
  "statSlots": [
@@ -156,12 +166,12 @@
156
166
  },
157
167
  {
158
168
  "outputItemId": 10006,
159
- "outputMass": 30000,
169
+ "outputMass": 420000,
160
170
  "inputs": [
161
171
  {
162
172
  "category": "crystal",
163
173
  "tier": 1,
164
- "quantity": 10
174
+ "quantity": 30
165
175
  }
166
176
  ],
167
177
  "statSlots": [
@@ -186,12 +196,12 @@
186
196
  },
187
197
  {
188
198
  "outputItemId": 10007,
189
- "outputMass": 30000,
199
+ "outputMass": 420000,
190
200
  "inputs": [
191
201
  {
192
202
  "category": "biomass",
193
203
  "tier": 1,
194
- "quantity": 32
204
+ "quantity": 25
195
205
  }
196
206
  ],
197
207
  "statSlots": [
@@ -216,12 +226,12 @@
216
226
  },
217
227
  {
218
228
  "outputItemId": 10008,
219
- "outputMass": 30000,
229
+ "outputMass": 400000,
220
230
  "inputs": [
221
231
  {
222
232
  "category": "regolith",
223
233
  "tier": 1,
224
- "quantity": 20
234
+ "quantity": 45
225
235
  }
226
236
  ],
227
237
  "statSlots": [
@@ -246,12 +256,17 @@
246
256
  },
247
257
  {
248
258
  "outputItemId": 10009,
249
- "outputMass": 50000,
259
+ "outputMass": 590000,
250
260
  "inputs": [
251
261
  {
252
262
  "category": "gas",
253
263
  "tier": 1,
254
- "quantity": 32
264
+ "quantity": 40
265
+ },
266
+ {
267
+ "category": "regolith",
268
+ "tier": 1,
269
+ "quantity": 40
255
270
  }
256
271
  ],
257
272
  "statSlots": [
@@ -276,12 +291,17 @@
276
291
  },
277
292
  {
278
293
  "outputItemId": 10010,
279
- "outputMass": 40000,
294
+ "outputMass": 590000,
280
295
  "inputs": [
281
296
  {
282
297
  "category": "crystal",
283
298
  "tier": 1,
284
- "quantity": 25
299
+ "quantity": 20
300
+ },
301
+ {
302
+ "category": "ore",
303
+ "tier": 1,
304
+ "quantity": 15
285
305
  }
286
306
  ],
287
307
  "statSlots": [
@@ -306,7 +326,7 @@
306
326
  },
307
327
  {
308
328
  "outputItemId": 10100,
309
- "outputMass": 150000,
329
+ "outputMass": 940000,
310
330
  "inputs": [
311
331
  {
312
332
  "itemId": 10003,
@@ -335,11 +355,11 @@
335
355
  },
336
356
  {
337
357
  "outputItemId": 10101,
338
- "outputMass": 75000,
358
+ "outputMass": 1440000,
339
359
  "inputs": [
340
360
  {
341
361
  "itemId": 10004,
342
- "quantity": 5
362
+ "quantity": 6
343
363
  }
344
364
  ],
345
365
  "statSlots": [
@@ -364,11 +384,11 @@
364
384
  },
365
385
  {
366
386
  "outputItemId": 10102,
367
- "outputMass": 145000,
387
+ "outputMass": 1220000,
368
388
  "inputs": [
369
389
  {
370
390
  "itemId": 10005,
371
- "quantity": 4
391
+ "quantity": 3
372
392
  },
373
393
  {
374
394
  "itemId": 10006,
@@ -392,9 +412,6 @@
392
412
  }
393
413
  ]
394
414
  },
395
- {
396
- "sources": []
397
- },
398
415
  {
399
416
  "sources": [
400
417
  {
@@ -416,22 +433,18 @@
416
433
  },
417
434
  {
418
435
  "outputItemId": 10103,
419
- "outputMass": 90000,
436
+ "outputMass": 1010000,
420
437
  "inputs": [
421
- {
422
- "itemId": 10002,
423
- "quantity": 3
424
- },
425
438
  {
426
439
  "itemId": 10007,
427
- "quantity": 3
440
+ "quantity": 6
428
441
  }
429
442
  ],
430
443
  "statSlots": [
431
444
  {
432
445
  "sources": [
433
446
  {
434
- "inputIndex": 1,
447
+ "inputIndex": 0,
435
448
  "statIndex": 1
436
449
  }
437
450
  ]
@@ -439,7 +452,7 @@
439
452
  {
440
453
  "sources": [
441
454
  {
442
- "inputIndex": 1,
455
+ "inputIndex": 0,
443
456
  "statIndex": 0
444
457
  }
445
458
  ]
@@ -449,7 +462,7 @@
449
462
  },
450
463
  {
451
464
  "outputItemId": 10104,
452
- "outputMass": 120000,
465
+ "outputMass": 1190000,
453
466
  "inputs": [
454
467
  {
455
468
  "itemId": 10008,
@@ -482,15 +495,15 @@
482
495
  },
483
496
  {
484
497
  "outputItemId": 10105,
485
- "outputMass": 100000,
498
+ "outputMass": 1220000,
486
499
  "inputs": [
487
500
  {
488
501
  "itemId": 10001,
489
- "quantity": 8
502
+ "quantity": 3
490
503
  },
491
504
  {
492
505
  "itemId": 10002,
493
- "quantity": 4
506
+ "quantity": 3
494
507
  }
495
508
  ],
496
509
  "statSlots": [
@@ -531,7 +544,7 @@
531
544
  },
532
545
  {
533
546
  "outputItemId": 10106,
534
- "outputMass": 120000,
547
+ "outputMass": 1430000,
535
548
  "inputs": [
536
549
  {
537
550
  "itemId": 10004,
@@ -566,24 +579,21 @@
566
579
  "statIndex": 1
567
580
  }
568
581
  ]
569
- },
570
- {
571
- "sources": []
572
582
  }
573
583
  ],
574
584
  "blendWeights": []
575
585
  },
576
586
  {
577
587
  "outputItemId": 10107,
578
- "outputMass": 180000,
588
+ "outputMass": 1420000,
579
589
  "inputs": [
580
590
  {
581
591
  "itemId": 10010,
582
- "quantity": 6
592
+ "quantity": 3
583
593
  },
584
594
  {
585
595
  "itemId": 10009,
586
- "quantity": 4
596
+ "quantity": 3
587
597
  }
588
598
  ],
589
599
  "statSlots": [
@@ -607,11 +617,11 @@
607
617
  },
608
618
  {
609
619
  "outputItemId": 10108,
610
- "outputMass": 90000,
620
+ "outputMass": 970000,
611
621
  "inputs": [
612
622
  {
613
623
  "itemId": 10003,
614
- "quantity": 4
624
+ "quantity": 3
615
625
  },
616
626
  {
617
627
  "itemId": 10007,
@@ -656,7 +666,7 @@
656
666
  },
657
667
  {
658
668
  "outputItemId": 10200,
659
- "outputMass": 80000,
669
+ "outputMass": 1490000,
660
670
  "inputs": [
661
671
  {
662
672
  "itemId": 10001,
@@ -705,7 +715,7 @@
705
715
  },
706
716
  {
707
717
  "outputItemId": 10201,
708
- "outputMass": 100000,
718
+ "outputMass": 2300000,
709
719
  "inputs": [
710
720
  {
711
721
  "itemId": 10001,
@@ -754,7 +764,7 @@
754
764
  },
755
765
  {
756
766
  "outputItemId": 10202,
757
- "outputMass": 1300000,
767
+ "outputMass": 5760000,
758
768
  "inputs": [
759
769
  {
760
770
  "itemId": 10001,
@@ -803,7 +813,7 @@
803
813
  },
804
814
  {
805
815
  "outputItemId": 10203,
806
- "outputMass": 800000,
816
+ "outputMass": 4440000,
807
817
  "inputs": [
808
818
  {
809
819
  "itemId": 10001,
@@ -852,7 +862,7 @@
852
862
  },
853
863
  {
854
864
  "outputItemId": 10204,
855
- "outputMass": 800000,
865
+ "outputMass": 5540000,
856
866
  "inputs": [
857
867
  {
858
868
  "itemId": 10001,
@@ -0,0 +1,45 @@
1
+ import {EntityClass, getKindMeta, getTemplateMeta} from '../data/kind-registry'
2
+ import {getRecipe} from '../data/recipes-runtime'
3
+ import {getItems} from '../data/catalog'
4
+ import type {Item} from '../types'
5
+
6
+ export type BuildMethod = 'craft+deploy' | 'plot'
7
+
8
+ export function availableBuildMethods(itemId: number): BuildMethod[] {
9
+ const recipe = getRecipe(itemId)
10
+ if (!recipe) return []
11
+
12
+ const template = getTemplateMeta(itemId)
13
+ if (!template) return ['craft+deploy']
14
+
15
+ const kindMeta = getKindMeta(template.kind)
16
+ if (!kindMeta) return ['craft+deploy']
17
+
18
+ if (kindMeta.classification === EntityClass.PlanetaryStructure) {
19
+ return ['craft+deploy', 'plot']
20
+ }
21
+ return ['craft+deploy']
22
+ }
23
+
24
+ export function isBuildable(itemId: number): boolean {
25
+ return availableBuildMethods(itemId).length > 0
26
+ }
27
+
28
+ export function isPlotBuildable(itemId: number): boolean {
29
+ return availableBuildMethods(itemId).includes('plot')
30
+ }
31
+
32
+ export function filterByBuildMethod<T extends {itemId: number}>(
33
+ items: T[],
34
+ method: BuildMethod
35
+ ): T[] {
36
+ return items.filter((i) => availableBuildMethods(i.itemId).includes(method))
37
+ }
38
+
39
+ export function allBuildableItems(): Item[] {
40
+ return getItems().filter((item) => isBuildable(item.id))
41
+ }
42
+
43
+ export function allPlotBuildableItems(): Item[] {
44
+ return getItems().filter((item) => isPlotBuildable(item.id))
45
+ }
@@ -10,7 +10,7 @@ export function computeShipHullCapabilities(stats: Record<string, number>): {
10
10
  const exponent = statSum / 2997.0
11
11
  return {
12
12
  hullmass: computeBaseHullmass(stats),
13
- capacity: Math.floor(1000000 * 10 ** exponent),
13
+ capacity: Math.floor(5000000 * 10 ** exponent),
14
14
  }
15
15
  }
16
16
 
@@ -185,8 +185,9 @@ export function computeBaseCapacity(itemId: number, stats: Record<string, number
185
185
  case ITEM_SHIP_T1_PACKED:
186
186
  case ITEM_EXTRACTOR_T1_PACKED:
187
187
  case ITEM_FACTORY_T1_PACKED:
188
- case ITEM_CONTAINER_T1_PACKED:
189
188
  return computeShipHullCapabilities(stats).capacity
189
+ case ITEM_CONTAINER_T1_PACKED:
190
+ return computeContainerCapabilities(stats).capacity
190
191
  case ITEM_WAREHOUSE_T1_PACKED:
191
192
  return computeWarehouseHullCapabilities(stats).capacity
192
193
  case ITEM_CONTAINER_T2_PACKED:
@@ -211,7 +212,7 @@ export function computeWarehouseHullCapabilities(stats: Record<string, number>):
211
212
  const exponent = statSum / 2997.0
212
213
  return {
213
214
  hullmass: computeBaseHullmass(stats),
214
- capacity: Math.floor(20000000 * 10 ** exponent),
215
+ capacity: Math.floor(100000000 * 10 ** exponent),
215
216
  }
216
217
  }
217
218
 
@@ -386,7 +387,12 @@ export function computeContainerCapabilities(stats: Record<string, number>): {
386
387
  hullmass: number
387
388
  capacity: number
388
389
  } {
389
- return computeShipHullCapabilities(stats)
390
+ const statSum = stats.strength + stats.hardness + stats.saturation
391
+ const exponent = statSum / 2997.0
392
+ return {
393
+ hullmass: computeBaseHullmass(stats),
394
+ capacity: Math.floor(20000000 * 10 ** exponent),
395
+ }
390
396
  }
391
397
 
392
398
  export function computeContainerT2Capabilities(stats: Record<string, number>): {
@@ -402,7 +408,7 @@ export function computeContainerT2Capabilities(stats: Record<string, number>): {
402
408
 
403
409
  const statSum = strength + hardness + saturation
404
410
  const exponent = statSum / 2500
405
- const capacity = Math.floor(1500000 * 10 ** exponent)
411
+ const capacity = Math.floor(40000000 * 10 ** exponent)
406
412
 
407
413
  return {hullmass, capacity}
408
414
  }
@@ -17,7 +17,9 @@ export {
17
17
  DEPTH_THRESHOLD_T5,
18
18
  LOCATION_MIN_DEPTH,
19
19
  LOCATION_MAX_DEPTH,
20
- YIELD_THRESHOLD,
20
+ yieldThresholdAt,
21
+ YIELD_FRACTION_SHALLOW,
22
+ YIELD_FRACTION_DEEP,
21
23
  PLANET_SUBTYPE_GAS_GIANT,
22
24
  PLANET_SUBTYPE_ROCKY,
23
25
  PLANET_SUBTYPE_TERRESTRIAL,
@@ -26,8 +28,11 @@ export {
26
28
  PLANET_SUBTYPE_INDUSTRIAL,
27
29
  } from './resources'
28
30
 
29
- export {RESERVE_TIERS, TIER_ROLL_MAX, rollTier, rollWithinTier} from './tiers'
31
+ export {RESERVE_TIERS, TIER_ROLL_MAX, tierOfReserve, rollTier, rollWithinTier} from './tiers'
30
32
  export type {ReserveTier, TierRange} from './tiers'
31
33
 
34
+ export {getEffectiveReserve} from './reserve-regen'
35
+ export type {EffectiveReserveInput} from './reserve-regen'
36
+
32
37
  export * from './stats'
33
38
  export * from './crafting'
@@ -0,0 +1,34 @@
1
+ import type {BlockTimestamp, UInt32} from '@wharfkit/antelope'
2
+
3
+ export interface EffectiveReserveInput {
4
+ remaining: UInt32 | number
5
+ max_reserve: UInt32 | number
6
+ last_block: BlockTimestamp
7
+ }
8
+
9
+ function toNumber(value: UInt32 | number): number {
10
+ return typeof value === 'number' ? value : Number(value)
11
+ }
12
+
13
+ function slotsBetween(now: BlockTimestamp, last: BlockTimestamp): number {
14
+ const nowMs = now.toMilliseconds()
15
+ const lastMs = last.toMilliseconds()
16
+ if (nowMs <= lastMs) return 0
17
+ return Math.floor((nowMs - lastMs) / 500)
18
+ }
19
+
20
+ export function getEffectiveReserve(
21
+ row: EffectiveReserveInput,
22
+ now: BlockTimestamp,
23
+ epochSeconds: number
24
+ ): number {
25
+ const remaining = toNumber(row.remaining)
26
+ const max = toNumber(row.max_reserve)
27
+ if (remaining >= max) return max
28
+ const epochSlots = epochSeconds * 2
29
+ if (epochSlots === 0) return remaining
30
+ const elapsed = slotsBetween(now, row.last_block)
31
+ const regen = Math.floor((max * elapsed) / epochSlots)
32
+ const effective = remaining + regen
33
+ return effective >= max ? max : effective
34
+ }
@@ -15,7 +15,15 @@ export const DEPTH_THRESHOLD_T10 = 63000
15
15
  export const LOCATION_MIN_DEPTH = 500
16
16
  export const LOCATION_MAX_DEPTH = 65535
17
17
 
18
- export const YIELD_THRESHOLD = Math.floor(0.001 * 0xffffffff)
18
+ export const YIELD_FRACTION_SHALLOW = 0.0025
19
+ export const YIELD_FRACTION_DEEP = 0.0005
20
+
21
+ export function yieldThresholdAt(stratum: number): number {
22
+ const clamped = stratum > 65535 ? 65535 : stratum
23
+ const t = clamped / 65535
24
+ const fraction = YIELD_FRACTION_SHALLOW + (YIELD_FRACTION_DEEP - YIELD_FRACTION_SHALLOW) * t
25
+ return Math.floor(fraction * 0xffffffff)
26
+ }
19
27
 
20
28
  export const PLANET_SUBTYPE_GAS_GIANT = 0
21
29
  export const PLANET_SUBTYPE_ROCKY = 1
@@ -1,7 +1,8 @@
1
1
  import {Bytes, Checksum256, type Checksum256Type} from '@wharfkit/antelope'
2
2
  import {hash512} from '../utils/hash'
3
3
  import {Coordinates, type CoordinatesType} from '../types'
4
- import {getEligibleResources, getResourceWeight, YIELD_THRESHOLD} from './resources'
4
+ import {getItem} from '../data/catalog'
5
+ import {getEligibleResources, getResourceWeight, yieldThresholdAt} from './resources'
5
6
  import {RESERVE_TIERS, rollTier, rollWithinTier} from './tiers'
6
7
 
7
8
  export interface StratumInfo {
@@ -33,15 +34,8 @@ export function deriveStratum(
33
34
 
34
35
  const rawReserve = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0
35
36
 
36
- let reserve = 0
37
- if (rawReserve <= YIELD_THRESHOLD) {
38
- const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
39
- const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
40
- const tier = rollTier(tierRoll, stratum)
41
- reserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier])
42
- }
43
-
44
- if (reserve === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
37
+ if (rawReserve > yieldThresholdAt(stratum))
38
+ return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
45
39
 
46
40
  const eligible = getEligibleResources(locationType, subtype, stratum)
47
41
  if (eligible.length === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
@@ -66,6 +60,12 @@ export function deriveStratum(
66
60
  }
67
61
  }
68
62
 
63
+ const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
64
+ const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
65
+ const tier = rollTier(tierRoll, stratum)
66
+ const unitMass = getItem(selectedItemId).mass
67
+ const reserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier], unitMass)
68
+
69
69
  const seedBigInt =
70
70
  (BigInt(bytes[8]) << 56n) |
71
71
  (BigInt(bytes[9]) << 48n) |
@@ -1,3 +1,5 @@
1
+ import {getItem} from '../data/catalog'
2
+
1
3
  export type ReserveTier = 'small' | 'medium' | 'large' | 'massive' | 'motherlode'
2
4
 
3
5
  export interface TierRange {
@@ -6,11 +8,11 @@ export interface TierRange {
6
8
  }
7
9
 
8
10
  export const RESERVE_TIERS: Record<ReserveTier, TierRange> = {
9
- small: {min: 15, max: 60},
10
- medium: {min: 100, max: 200},
11
- large: {min: 400, max: 700},
12
- massive: {min: 1000, max: 2500},
13
- motherlode: {min: 4000, max: 10000},
11
+ small: {min: 3_600_000, max: 14_400_000},
12
+ medium: {min: 24_000_000, max: 48_000_000},
13
+ large: {min: 96_000_000, max: 168_000_000},
14
+ massive: {min: 240_000_000, max: 600_000_000},
15
+ motherlode: {min: 960_000_000, max: 2_400_000_000},
14
16
  }
15
17
 
16
18
  const SHALLOW_THRESHOLDS = {
@@ -47,8 +49,27 @@ export function rollTier(tierRoll: number, stratum: number): ReserveTier {
47
49
  return 'motherlode'
48
50
  }
49
51
 
50
- export function rollWithinTier(withinRoll: number, range: TierRange): number {
52
+ export function rollWithinTier(
53
+ withinRoll: number,
54
+ range: TierRange,
55
+ resourceUnitMass: number
56
+ ): number {
51
57
  const u = withinRoll / 65535
52
58
  const skewed = u * u
53
- return Math.floor(range.min + skewed * (range.max - range.min))
59
+ const depositMass = range.min + skewed * (range.max - range.min)
60
+ return Math.max(1, Math.floor(depositMass / resourceUnitMass))
61
+ }
62
+
63
+ const RESERVE_TIER_ENTRIES = Object.entries(RESERVE_TIERS) as Array<[ReserveTier, TierRange]>
64
+
65
+ export function tierOfReserve(reserve: number, itemId: number): ReserveTier | null {
66
+ if (reserve <= 0) return null
67
+ const unitMass = getItem(itemId).mass
68
+ if (unitMass <= 0) return null
69
+ const impliedMassLow = reserve * unitMass
70
+ const impliedMassHigh = impliedMassLow + unitMass
71
+ for (const [tier, range] of RESERVE_TIER_ENTRIES) {
72
+ if (impliedMassHigh > range.min && impliedMassLow <= range.max) return tier
73
+ }
74
+ return null
54
75
  }
@@ -22,6 +22,7 @@ export interface EntityStateInput {
22
22
  owner: NameType
23
23
  name: string
24
24
  coordinates: {x: number; y: number; z?: number}
25
+ itemId?: number
25
26
  hullmass?: number
26
27
  capacity?: number
27
28
  cargomass?: number
@@ -104,6 +105,7 @@ export function makeEntity(packedItemId: number, state: EntityStateInput): Entit
104
105
  owner: Name.from(state.owner),
105
106
  entity_name: state.name,
106
107
  coordinates: ServerContract.Types.coordinates.from(state.coordinates),
108
+ item_id: UInt16.from(state.itemId ?? template.itemId),
107
109
  cargomass: UInt32.from(state.cargomass ?? 0),
108
110
  cargo: state.cargo || [],
109
111
  is_idle: !state.schedule,