@shipload/sdk 2.0.0-rc13 → 2.0.0-rc15

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.
@@ -8,7 +8,7 @@ export interface StatDefinition {
8
8
  inverted?: boolean
9
9
  }
10
10
 
11
- const METAL_STATS: StatDefinition[] = [
11
+ const ORE_STATS: StatDefinition[] = [
12
12
  {
13
13
  key: 'strength',
14
14
  label: 'Strength',
@@ -30,24 +30,24 @@ const METAL_STATS: StatDefinition[] = [
30
30
  },
31
31
  ]
32
32
 
33
- const PRECIOUS_STATS: StatDefinition[] = [
33
+ const CRYSTAL_STATS: StatDefinition[] = [
34
34
  {
35
35
  key: 'conductivity',
36
36
  label: 'Conductivity',
37
37
  abbreviation: 'CON',
38
- purpose: 'Efficiency of energy/signal transfer',
38
+ purpose: 'Efficiency of energy/signal transfer through crystalline lattice',
39
39
  },
40
40
  {
41
- key: 'ductility',
42
- label: 'Ductility',
43
- abbreviation: 'DUC',
44
- purpose: 'Ability to be worked into fine, precise shapes',
41
+ key: 'resonance',
42
+ label: 'Resonance',
43
+ abbreviation: 'RES',
44
+ purpose: 'Frequency tuning and piezoelectric response storage and amplification',
45
45
  },
46
46
  {
47
47
  key: 'reflectivity',
48
48
  label: 'Reflectivity',
49
49
  abbreviation: 'REF',
50
- purpose: 'Surface quality for heat management and precision optics',
50
+ purpose: 'Optical refraction and reflection lenses, mirrors, focus',
51
51
  },
52
52
  ]
53
53
 
@@ -72,54 +72,54 @@ const GAS_STATS: StatDefinition[] = [
72
72
  },
73
73
  ]
74
74
 
75
- const MINERAL_STATS: StatDefinition[] = [
75
+ const REGOLITH_STATS: StatDefinition[] = [
76
76
  {
77
- key: 'resonance',
78
- label: 'Resonance',
79
- abbreviation: 'RES',
80
- purpose: 'Energy field interaction storage, focusing, projection',
77
+ key: 'composition',
78
+ label: 'Composition',
79
+ abbreviation: 'COM',
80
+ purpose: 'Mineral/metal mixdrives sensor, chip, and optic crafting quality',
81
81
  },
82
82
  {
83
83
  key: 'hardness',
84
84
  label: 'Hardness',
85
85
  abbreviation: 'HRD',
86
- purpose: 'Resistance to wear — cutting surfaces, penetration',
86
+ purpose: 'Particle hardness — cutting surfaces, abrasives, wear resistance',
87
87
  },
88
88
  {
89
- key: 'clarity',
90
- label: 'Clarity',
91
- abbreviation: 'CLR',
92
- purpose: 'Crystalline perfectionprecision optics',
89
+ key: 'fineness',
90
+ label: 'Fineness',
91
+ abbreviation: 'FIN',
92
+ purpose: 'Grain sizefine powder for smooth composites and sintering',
93
93
  },
94
94
  ]
95
95
 
96
- const ORGANIC_STATS: StatDefinition[] = [
96
+ const BIOMASS_STATS: StatDefinition[] = [
97
97
  {
98
98
  key: 'plasticity',
99
99
  label: 'Plasticity',
100
100
  abbreviation: 'PLA',
101
- purpose: 'Ease of reshaping speeds processing',
101
+ purpose: 'Flexibility and deformation under load',
102
102
  },
103
103
  {
104
104
  key: 'insulation',
105
105
  label: 'Insulation',
106
106
  abbreviation: 'INS',
107
- purpose: 'Energy containment reduces energy loss',
107
+ purpose: 'Thermal and electrical blocking capacity',
108
108
  },
109
109
  {
110
- key: 'purity',
111
- label: 'Purity',
112
- abbreviation: 'PUR',
113
- purpose: 'Biological cleanliness better composites and lubricants',
110
+ key: 'saturation',
111
+ label: 'Saturation',
112
+ abbreviation: 'SAT',
113
+ purpose: 'Concentration of useful organic compounds per unit',
114
114
  },
115
115
  ]
116
116
 
117
117
  const STAT_MAP: Record<ResourceCategory, StatDefinition[]> = {
118
- metal: METAL_STATS,
119
- precious: PRECIOUS_STATS,
118
+ ore: ORE_STATS,
119
+ crystal: CRYSTAL_STATS,
120
120
  gas: GAS_STATS,
121
- mineral: MINERAL_STATS,
122
- organic: ORGANIC_STATS,
121
+ regolith: REGOLITH_STATS,
122
+ biomass: BIOMASS_STATS,
123
123
  }
124
124
 
125
125
  export function getStatDefinitions(category: ResourceCategory): StatDefinition[] {
@@ -89,6 +89,18 @@ export function deriveStratum(
89
89
  return {itemId: selectedItemId, seed: seedBigInt, richness, reserve}
90
90
  }
91
91
 
92
+ /**
93
+ * Derives the three stat values for a raw resource from a deposit's
94
+ * entropy seed (hash-based, weibull-transformed).
95
+ *
96
+ * **Use only on deposit seeds** — the bigint returned by `deriveStratum`
97
+ * or carried on a `MassDeposit`. Do NOT call this on a cargo item's
98
+ * `stats` field; cargo stats are bit-packed and must be read via
99
+ * `decodeStat` (or `decodeStackStats` for category-mapped output).
100
+ *
101
+ * Passing a cargo `stats` value here produces meaningless output
102
+ * (hash of the packed bits, unrelated to the actual stats).
103
+ */
92
104
  export function deriveResourceStats(seed: bigint): ResourceStats {
93
105
  const seedBytes = new Uint8Array(8)
94
106
  for (let i = 0; i < 8; i++) {
@@ -13,6 +13,7 @@ export interface ContainerStateInput {
13
13
  hullmass: number
14
14
  capacity: number
15
15
  cargomass?: number
16
+ cargo?: ServerContract.Types.cargo_item[]
16
17
  schedule?: ServerContract.Types.schedule
17
18
  }
18
19
 
@@ -75,12 +76,12 @@ export function computeContainerCapabilities(stats: Record<string, number>): {
75
76
  } {
76
77
  const density = stats['density'] ?? 500
77
78
  const strength = stats['strength'] ?? 500
78
- const ductility = stats['ductility'] ?? 500
79
- const purity = stats['purity'] ?? 500
79
+ const fineness = stats['fineness'] ?? 500
80
+ const saturation = stats['saturation'] ?? 500
80
81
 
81
82
  const hullmass = 25000 + 75 * density
82
83
 
83
- const statSum = strength + ductility + purity
84
+ const statSum = strength + fineness + saturation
84
85
  const exponent = statSum / 2997
85
86
  const capacity = Math.floor(1000000 * Math.pow(10, exponent))
86
87
 
@@ -93,12 +94,12 @@ export function computeContainerT2Capabilities(stats: Record<string, number>): {
93
94
  } {
94
95
  const strength = stats['strength'] ?? 0
95
96
  const density = stats['density'] ?? 0
96
- const ductility = stats['ductility'] ?? 0
97
- const purity = stats['purity'] ?? 0
97
+ const fineness = stats['fineness'] ?? 0
98
+ const saturation = stats['saturation'] ?? 0
98
99
 
99
100
  const hullmass = 20000 + 50 * density
100
101
 
101
- const statSum = strength + ductility + purity
102
+ const statSum = strength + fineness + saturation
102
103
  const exponent = statSum / 2500
103
104
  const capacity = Math.floor(1500000 * Math.pow(10, exponent))
104
105
 
@@ -1,8 +1,86 @@
1
- import {Name, UInt16, UInt32, UInt64} from '@wharfkit/antelope'
1
+ import {Name, UInt16, UInt32, UInt64, UInt8} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
- import {Ship, ShipStateInput} from './ship'
4
- import {Warehouse, WarehouseStateInput} from './warehouse'
3
+ import {PackedModuleInput, Ship, ShipStateInput} from './ship'
4
+ import {computeWarehouseCapabilities, Warehouse, WarehouseStateInput} from './warehouse'
5
5
  import {Container, ContainerStateInput} from './container'
6
+ import {
7
+ getEntitySlotLayout,
8
+ getModuleRecipeByItemId,
9
+ ITEM_SHIP_T1_PACKED,
10
+ ITEM_WAREHOUSE_T1_PACKED,
11
+ } from '../data/recipes'
12
+ import {getModuleCapabilityType, MODULE_STORAGE, moduleAccepts} from '../capabilities/modules'
13
+ import {computeShipCapabilities, computeStorageCapabilities} from './ship-deploy'
14
+ import {decodeCraftedItemStats} from '../derivation/crafting'
15
+
16
+ function assignModulesToSlots(
17
+ packedEntityItemId: number,
18
+ modules: PackedModuleInput[],
19
+ entityLabel: string
20
+ ): ServerContract.Types.module_entry[] {
21
+ const slots = getEntitySlotLayout(packedEntityItemId)
22
+ const result: Array<{type: number; installed?: ServerContract.Types.packed_module}> = slots.map(
23
+ (s) => ({type: s.type, installed: undefined})
24
+ )
25
+
26
+ for (const mod of modules) {
27
+ const itemId = Number(UInt16.from(mod.itemId).value.toString())
28
+ const modType = getModuleCapabilityType(itemId)
29
+ const slotIdx = result.findIndex((r) => !r.installed && moduleAccepts(r.type, modType))
30
+ if (slotIdx === -1) {
31
+ const recipe = getModuleRecipeByItemId(itemId)
32
+ const modName = recipe?.name ?? `item ${itemId}`
33
+ throw new Error(
34
+ `No compatible slot for module ${modName} (type ${modType}) on ${entityLabel}`
35
+ )
36
+ }
37
+ result[slotIdx].installed = ServerContract.Types.packed_module.from({
38
+ item_id: UInt16.from(mod.itemId),
39
+ stats: UInt64.from(mod.stats),
40
+ })
41
+ }
42
+
43
+ return result.map((r) =>
44
+ ServerContract.Types.module_entry.from({
45
+ type: UInt8.from(r.type),
46
+ installed: r.installed,
47
+ })
48
+ )
49
+ }
50
+
51
+ function decodePackedInput(m: PackedModuleInput): {itemId: number; stats: bigint} {
52
+ return {
53
+ itemId: Number(UInt16.from(m.itemId).value.toString()),
54
+ stats: BigInt(UInt64.from(m.stats).toString()),
55
+ }
56
+ }
57
+
58
+ function computeStorageBonus(
59
+ decoded: {itemId: number; stats: bigint}[],
60
+ baseCapacity: number
61
+ ): number {
62
+ let totalBonus = 0
63
+ for (const m of decoded) {
64
+ if (getModuleCapabilityType(m.itemId) !== MODULE_STORAGE) continue
65
+ const stats = decodeCraftedItemStats(m.itemId, m.stats)
66
+ const {capacityBonus} = computeStorageCapabilities(stats, baseCapacity)
67
+ totalBonus += capacityBonus
68
+ }
69
+ return totalBonus
70
+ }
71
+
72
+ function deriveShipFromModules(
73
+ modules: PackedModuleInput[],
74
+ baseCapacity: number
75
+ ): {
76
+ capabilities: ReturnType<typeof computeShipCapabilities>
77
+ finalCapacity: number
78
+ } {
79
+ const decoded = modules.map(decodePackedInput)
80
+ const capabilities = computeShipCapabilities(decoded)
81
+ const totalBonus = computeStorageBonus(decoded, baseCapacity)
82
+ return {capabilities, finalCapacity: baseCapacity + totalBonus}
83
+ }
6
84
 
7
85
  export function makeShip(state: ShipStateInput): Ship {
8
86
  const info: Record<string, unknown> = {
@@ -19,14 +97,32 @@ export function makeShip(state: ShipStateInput): Ship {
19
97
  pending_tasks: [],
20
98
  }
21
99
  if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
22
- if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
23
100
  if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
24
- if (state.engines) info.engines = state.engines
25
- if (state.generator) info.generator = state.generator
26
- if (state.loaders) info.loaders = state.loaders
27
101
  if (state.schedule) info.schedule = state.schedule
102
+
103
+ let moduleEntries: ServerContract.Types.module_entry[] = []
104
+ if (state.modules && state.modules.length > 0) {
105
+ moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, state.modules, 'Ship T1')
106
+ const {capabilities, finalCapacity} = deriveShipFromModules(
107
+ state.modules,
108
+ state.capacity ?? 0
109
+ )
110
+ if (capabilities.engines) info.engines = capabilities.engines
111
+ if (capabilities.generator) info.generator = capabilities.generator
112
+ if (capabilities.gatherer) info.gatherer = capabilities.gatherer
113
+ if (capabilities.hauler) info.hauler = capabilities.hauler
114
+ if (capabilities.loaders) info.loaders = capabilities.loaders
115
+ if (capabilities.crafter) info.crafter = capabilities.crafter
116
+ if (state.capacity !== undefined) info.capacity = UInt32.from(finalCapacity)
117
+ } else {
118
+ moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, [], 'Ship T1')
119
+ if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
120
+ }
121
+
28
122
  const entityInfo = ServerContract.Types.entity_info.from(info)
29
- return new Ship(entityInfo)
123
+ const ship = new Ship(entityInfo)
124
+ ship.setModules(moduleEntries)
125
+ return ship
30
126
  }
31
127
 
32
128
  export function makeWarehouse(state: WarehouseStateInput): Warehouse {
@@ -45,10 +141,29 @@ export function makeWarehouse(state: WarehouseStateInput): Warehouse {
45
141
  pending_tasks: [],
46
142
  }
47
143
  if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
48
- if (state.loaders) info.loaders = state.loaders
49
144
  if (state.schedule) info.schedule = state.schedule
145
+
146
+ let moduleEntries: ServerContract.Types.module_entry[] = []
147
+ if (state.modules && state.modules.length > 0) {
148
+ moduleEntries = assignModulesToSlots(
149
+ ITEM_WAREHOUSE_T1_PACKED,
150
+ state.modules,
151
+ 'Warehouse T1'
152
+ )
153
+ const decoded = state.modules.map(decodePackedInput)
154
+ const capabilities = computeWarehouseCapabilities(decoded)
155
+ if (capabilities.loaders) info.loaders = capabilities.loaders
156
+
157
+ const totalBonus = computeStorageBonus(decoded, state.capacity)
158
+ info.capacity = UInt32.from(state.capacity + totalBonus)
159
+ } else {
160
+ moduleEntries = assignModulesToSlots(ITEM_WAREHOUSE_T1_PACKED, [], 'Warehouse T1')
161
+ }
162
+
50
163
  const entityInfo = ServerContract.Types.entity_info.from(info)
51
- return new Warehouse(entityInfo)
164
+ const warehouse = new Warehouse(entityInfo)
165
+ warehouse.setModules(moduleEntries)
166
+ return warehouse
52
167
  }
53
168
 
54
169
  export function makeContainer(state: ContainerStateInput): Container {
@@ -61,7 +176,7 @@ export function makeContainer(state: ContainerStateInput): Container {
61
176
  hullmass: UInt32.from(state.hullmass),
62
177
  capacity: UInt32.from(state.capacity),
63
178
  cargomass: UInt32.from(state.cargomass || 0),
64
- cargo: [],
179
+ cargo: state.cargo || [],
65
180
  is_idle: !state.schedule,
66
181
  current_task_elapsed: UInt32.from(0),
67
182
  current_task_remaining: UInt32.from(0),
@@ -15,11 +15,11 @@ export function computeShipHullCapabilities(stats: Record<string, number>): {
15
15
  } {
16
16
  const density = stats.density ?? 500
17
17
  const strength = stats.strength ?? 500
18
- const ductility = stats.ductility ?? 500
19
- const purity = stats.purity ?? 500
18
+ const fineness = stats.fineness ?? 500
19
+ const saturation = stats.saturation ?? 500
20
20
 
21
21
  const hullmass = 25000 + 75 * density
22
- const statSum = strength + ductility + purity
22
+ const statSum = strength + fineness + saturation
23
23
  const exponent = statSum / 2997.0
24
24
  const capacity = Math.floor(1000000 * Math.pow(10, exponent))
25
25
 
@@ -44,11 +44,11 @@ export function computeGeneratorCapabilities(stats: Record<string, number>): {
44
44
  recharge: number
45
45
  } {
46
46
  const res = stats.resonance ?? 500
47
- const clr = stats.clarity ?? 500
47
+ const ref = stats.reflectivity ?? 500
48
48
 
49
49
  return {
50
50
  capacity: 300 + Math.floor(res / 6),
51
- recharge: 5 + Math.floor((clr * 15) / 1000),
51
+ recharge: 1 + Math.floor((ref * 3) / 1000),
52
52
  }
53
53
  }
54
54
 
@@ -65,7 +65,7 @@ export function computeGathererCapabilities(stats: Record<string, number>): {
65
65
 
66
66
  return {
67
67
  yield: 200 + str,
68
- drain: Math.max(10, 50 - Math.floor(con / 20)),
68
+ drain: Math.max(250, 1250 - Math.floor((con * 25) / 20)),
69
69
  depth: 200 + Math.floor((tol * 3) / 2),
70
70
  speed: 100 + Math.floor((ref * 4) / 5),
71
71
  }
@@ -76,11 +76,11 @@ export function computeLoaderCapabilities(stats: Record<string, number>): {
76
76
  thrust: number
77
77
  quantity: number
78
78
  } {
79
- const duc = stats.ductility ?? 500
79
+ const fin = stats.fineness ?? 500
80
80
  const pla = stats.plasticity ?? 500
81
81
 
82
82
  return {
83
- mass: Math.max(200, 2000 - Math.floor(duc * 2)),
83
+ mass: Math.max(200, 2000 - Math.floor(fin * 2)),
84
84
  thrust: 1 + Math.floor(pla / 500),
85
85
  quantity: 1,
86
86
  }
@@ -91,11 +91,11 @@ export function computeManufacturingCapabilities(stats: Record<string, number>):
91
91
  drain: number
92
92
  } {
93
93
  const rea = stats.reactivity ?? 500
94
- const clr = stats.clarity ?? 500
94
+ const com = stats.composition ?? 500
95
95
 
96
96
  return {
97
97
  speed: 100 + Math.floor((rea * 4) / 5),
98
- drain: Math.max(5, 30 - Math.floor(clr / 33)),
98
+ drain: Math.max(5, 30 - Math.floor(com / 33)),
99
99
  }
100
100
  }
101
101
 
@@ -106,12 +106,12 @@ export function computeHaulerCapabilities(stats: Record<string, number>): {
106
106
  } {
107
107
  const res = stats.resonance ?? 500
108
108
  const con = stats.conductivity ?? 500
109
- const clr = stats.clarity ?? 500
109
+ const ref = stats.reflectivity ?? 500
110
110
 
111
111
  return {
112
112
  capacity: Math.max(1, 1 + Math.floor(res / 400)),
113
113
  efficiency: 2000 + con * 6,
114
- drain: Math.max(3, 15 - Math.floor(clr / 80)),
114
+ drain: Math.max(3, 15 - Math.floor(ref / 80)),
115
115
  }
116
116
  }
117
117
 
@@ -122,10 +122,10 @@ export function computeStorageCapabilities(
122
122
  capacityBonus: number
123
123
  } {
124
124
  const strength = stats.strength ?? 500
125
- const ductility = stats.ductility ?? 500
126
- const purity = stats.purity ?? 500
125
+ const fineness = stats.fineness ?? 500
126
+ const saturation = stats.saturation ?? 500
127
127
 
128
- const statSum = strength + ductility + purity
128
+ const statSum = strength + fineness + saturation
129
129
  const capacityBonus = Math.floor(
130
130
  (baseCapacity * (10 + Math.floor((statSum * 10) / 2997))) / 100
131
131
  )
@@ -139,11 +139,11 @@ export function computeWarehouseHullCapabilities(stats: Record<string, number>):
139
139
  } {
140
140
  const density = stats.density ?? 500
141
141
  const strength = stats.strength ?? 500
142
- const ductility = stats.ductility ?? 500
143
- const purity = stats.purity ?? 500
142
+ const fineness = stats.fineness ?? 500
143
+ const saturation = stats.saturation ?? 500
144
144
 
145
145
  const hullmass = 25000 + 75 * density
146
- const statSum = strength + ductility + purity
146
+ const statSum = strength + fineness + saturation
147
147
  const exponent = statSum / 2997.0
148
148
  const capacity = Math.floor(20000000 * Math.pow(10, exponent))
149
149
 
@@ -160,7 +160,7 @@ export interface ShipCapabilities {
160
160
  }
161
161
 
162
162
  export function computeShipCapabilities(
163
- modules: {itemId: number; seed: bigint}[]
163
+ modules: {itemId: number; stats: bigint}[]
164
164
  ): ShipCapabilities {
165
165
  const ship: ShipCapabilities = {}
166
166
 
@@ -169,7 +169,7 @@ export function computeShipCapabilities(
169
169
  let totalThrust = 0
170
170
  let totalDrain = 0
171
171
  for (const m of engineModules) {
172
- const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.seed))
172
+ const caps = computeEngineCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
173
173
  totalThrust += caps.thrust
174
174
  totalDrain += caps.drain
175
175
  }
@@ -183,7 +183,7 @@ export function computeShipCapabilities(
183
183
  let totalCapacity = 0
184
184
  let totalRecharge = 0
185
185
  for (const m of generatorModules) {
186
- const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.seed))
186
+ const caps = computeGeneratorCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
187
187
  totalCapacity += caps.capacity
188
188
  totalRecharge += caps.recharge
189
189
  }
@@ -199,7 +199,7 @@ export function computeShipCapabilities(
199
199
  let totalDepth = 0
200
200
  let totalSpeed = 0
201
201
  for (const m of gathererModules) {
202
- const caps = computeGathererCapabilities(decodeCraftedItemStats(m.itemId, m.seed))
202
+ const caps = computeGathererCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
203
203
  totalYield += caps.yield
204
204
  totalDrain += caps.drain
205
205
  totalDepth += caps.depth
@@ -214,12 +214,7 @@ export function computeShipCapabilities(
214
214
  let weightedEffNum = 0
215
215
  let totalDrain = 0
216
216
  for (const m of haulerModules) {
217
- const decoded = decodeCraftedItemStats(m.itemId, m.seed)
218
- const caps = computeHaulerCapabilities({
219
- resonance: decoded.capacity,
220
- conductivity: decoded.efficiency,
221
- clarity: decoded.drain,
222
- })
217
+ const caps = computeHaulerCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
223
218
  totalCapacity += caps.capacity
224
219
  weightedEffNum += caps.efficiency * caps.capacity
225
220
  totalDrain += caps.drain
@@ -237,7 +232,7 @@ export function computeShipCapabilities(
237
232
  let totalThrust = 0
238
233
  let totalQuantity = 0
239
234
  for (const m of loaderModules) {
240
- const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.seed))
235
+ const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
241
236
  totalMass += caps.mass
242
237
  totalThrust += caps.thrust
243
238
  totalQuantity += caps.quantity
@@ -252,7 +247,7 @@ export function computeShipCapabilities(
252
247
  let totalSpeed = 0
253
248
  let totalDrain = 0
254
249
  for (const m of crafterModules) {
255
- const caps = computeManufacturingCapabilities(decodeCraftedItemStats(m.itemId, m.seed))
250
+ const caps = computeManufacturingCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
256
251
  totalSpeed += caps.speed
257
252
  totalDrain += caps.drain
258
253
  }
@@ -1,4 +1,4 @@
1
- import {UInt16, UInt32, UInt64, UInt64Type} from '@wharfkit/antelope'
1
+ import {UInt16, UInt16Type, UInt32, UInt64, UInt64Type} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
3
  import {Coordinates, CoordinatesType} from '../types'
4
4
  import {
@@ -23,6 +23,11 @@ import {
23
23
  } from '../capabilities/movement'
24
24
  import * as schedule from '../scheduling/schedule'
25
25
 
26
+ export interface PackedModuleInput {
27
+ itemId: UInt16Type
28
+ stats: UInt64Type
29
+ }
30
+
26
31
  export interface ShipStateInput {
27
32
  id: UInt64Type
28
33
  owner: string
@@ -31,9 +36,7 @@ export interface ShipStateInput {
31
36
  hullmass?: number
32
37
  capacity?: number
33
38
  energy?: number
34
- engines?: ServerContract.Types.movement_stats
35
- generator?: ServerContract.Types.energy_stats
36
- loaders?: ServerContract.Types.loader_stats
39
+ modules?: PackedModuleInput[]
37
40
  schedule?: ServerContract.Types.schedule
38
41
  cargo?: ServerContract.Types.cargo_item[]
39
42
  }
@@ -47,11 +50,20 @@ type MovementEntity = {
47
50
  export class Ship extends ServerContract.Types.entity_info {
48
51
  private _sched?: ScheduleAccessor
49
52
  private _inv?: InventoryAccessor
53
+ private _modules: ServerContract.Types.module_entry[] = []
50
54
 
51
55
  get name(): string {
52
56
  return this.entity_name
53
57
  }
54
58
 
59
+ get modules(): ServerContract.Types.module_entry[] {
60
+ return this._modules
61
+ }
62
+
63
+ setModules(modules: ServerContract.Types.module_entry[]): void {
64
+ this._modules = modules
65
+ }
66
+
55
67
  get inv(): InventoryAccessor {
56
68
  return (this._inv ??= new InventoryAccessor(this))
57
69
  }
@@ -6,6 +6,10 @@ import {ScheduleAccessor} from '../scheduling/accessor'
6
6
  import {InventoryAccessor} from './inventory-accessor'
7
7
  import {EntityInventory} from './entity-inventory'
8
8
  import * as schedule from '../scheduling/schedule'
9
+ import type {PackedModuleInput} from './ship'
10
+ import {decodeCraftedItemStats} from '../derivation/crafting'
11
+ import {getModuleCapabilityType, MODULE_LOADER} from '../capabilities/modules'
12
+ import {computeLoaderCapabilities} from './ship-deploy'
9
13
 
10
14
  export interface WarehouseStateInput {
11
15
  id: UInt64Type
@@ -14,7 +18,7 @@ export interface WarehouseStateInput {
14
18
  coordinates: CoordinatesType | {x: number; y: number; z?: number}
15
19
  hullmass?: number
16
20
  capacity: number
17
- loaders?: ServerContract.Types.loader_stats
21
+ modules?: PackedModuleInput[]
18
22
  schedule?: ServerContract.Types.schedule
19
23
  cargo?: ServerContract.Types.cargo_item[]
20
24
  }
@@ -22,11 +26,20 @@ export interface WarehouseStateInput {
22
26
  export class Warehouse extends ServerContract.Types.entity_info {
23
27
  private _sched?: ScheduleAccessor
24
28
  private _inv?: InventoryAccessor
29
+ private _modules: ServerContract.Types.module_entry[] = []
25
30
 
26
31
  get name(): string {
27
32
  return this.entity_name
28
33
  }
29
34
 
35
+ get modules(): ServerContract.Types.module_entry[] {
36
+ return this._modules
37
+ }
38
+
39
+ setModules(modules: ServerContract.Types.module_entry[]): void {
40
+ this._modules = modules
41
+ }
42
+
30
43
  get inv(): InventoryAccessor {
31
44
  return (this._inv ??= new InventoryAccessor(this))
32
45
  }
@@ -89,3 +102,25 @@ export class Warehouse extends ServerContract.Types.entity_info {
89
102
  return hull.adding(this.totalCargoMass)
90
103
  }
91
104
  }
105
+
106
+ export function computeWarehouseCapabilities(modules: {itemId: number; stats: bigint}[]): {
107
+ loaders?: {mass: number; thrust: number; quantity: number}
108
+ } {
109
+ const warehouse: {loaders?: {mass: number; thrust: number; quantity: number}} = {}
110
+
111
+ const loaderModules = modules.filter((m) => getModuleCapabilityType(m.itemId) === MODULE_LOADER)
112
+ if (loaderModules.length > 0) {
113
+ let totalMass = 0
114
+ let totalThrust = 0
115
+ let totalQuantity = 0
116
+ for (const m of loaderModules) {
117
+ const caps = computeLoaderCapabilities(decodeCraftedItemStats(m.itemId, m.stats))
118
+ totalMass += caps.mass
119
+ totalThrust += caps.thrust
120
+ totalQuantity += caps.quantity
121
+ }
122
+ warehouse.loaders = {mass: totalMass, thrust: totalThrust, quantity: totalQuantity}
123
+ }
124
+
125
+ return warehouse
126
+ }
package/src/format.ts ADDED
@@ -0,0 +1,12 @@
1
+ export function formatMass(kg: number): string {
2
+ const t = kg / 1000
3
+ const fixed = t.toFixed(2)
4
+ const trimmed = fixed.replace(/\.?0+$/, '')
5
+ return `${trimmed} t`
6
+ }
7
+
8
+ export function formatMassDelta(kg: number): string {
9
+ if (kg === 0) return '0 t'
10
+ const sign = kg > 0 ? '+' : '-'
11
+ return `${sign}${formatMass(Math.abs(kg))}`
12
+ }