@shipload/sdk 1.0.0-next.6 → 1.0.0-next.61

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 (156) hide show
  1. package/lib/scan.d.ts +52 -0
  2. package/lib/scan.js +162 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +152 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +3601 -1364
  7. package/lib/shipload.js +15671 -5261
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +15392 -5209
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1173 -0
  12. package/lib/testing.js +4971 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4965 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.test.ts +82 -0
  18. package/src/capabilities/craftable.ts +82 -0
  19. package/src/capabilities/crafting.test.ts +35 -0
  20. package/src/capabilities/crafting.ts +34 -7
  21. package/src/capabilities/gathering.test.ts +29 -0
  22. package/src/capabilities/gathering.ts +37 -19
  23. package/src/capabilities/hauling.ts +0 -5
  24. package/src/capabilities/index.ts +0 -1
  25. package/src/capabilities/modules.ts +27 -30
  26. package/src/capabilities/movement.ts +1 -1
  27. package/src/capabilities/storage.ts +16 -1
  28. package/src/contracts/platform.ts +231 -3
  29. package/src/contracts/server.ts +1518 -501
  30. package/src/coordinates/address.ts +88 -0
  31. package/src/coordinates/constants.test.ts +15 -0
  32. package/src/coordinates/constants.ts +23 -0
  33. package/src/coordinates/index.ts +15 -0
  34. package/src/coordinates/memo.test.ts +47 -0
  35. package/src/coordinates/memo.ts +20 -0
  36. package/src/coordinates/permutation.ts +77 -0
  37. package/src/coordinates/regions.ts +48 -0
  38. package/src/coordinates/sectors.ts +115 -0
  39. package/src/data/capabilities.ts +49 -18
  40. package/src/data/capability-formulas.ts +26 -14
  41. package/src/data/catalog.ts +7 -8
  42. package/src/data/colors.ts +14 -48
  43. package/src/data/entities.json +380 -15
  44. package/src/data/item-ids.ts +51 -13
  45. package/src/data/items.json +318 -77
  46. package/src/data/kind-registry.json +195 -0
  47. package/src/data/kind-registry.ts +182 -0
  48. package/src/data/metadata.ts +291 -51
  49. package/src/data/recipes-runtime.ts +6 -23
  50. package/src/data/recipes.json +2260 -173
  51. package/src/data/tiers.ts +9 -6
  52. package/src/derivation/build-methods.test.ts +13 -0
  53. package/src/derivation/build-methods.ts +48 -0
  54. package/src/derivation/capabilities.test.ts +348 -0
  55. package/src/derivation/capabilities.ts +693 -0
  56. package/src/derivation/capability-mappings.ts +60 -16
  57. package/src/derivation/crafting.test.ts +17 -0
  58. package/src/derivation/crafting.ts +57 -34
  59. package/src/derivation/index.ts +27 -2
  60. package/src/derivation/recipe-usage.test.ts +90 -0
  61. package/src/derivation/recipe-usage.ts +141 -0
  62. package/src/derivation/reserve-regen.ts +34 -0
  63. package/src/derivation/resources.ts +9 -1
  64. package/src/derivation/rollups.test.ts +55 -0
  65. package/src/derivation/rollups.ts +72 -0
  66. package/src/derivation/stars.test.ts +67 -0
  67. package/src/derivation/stars.ts +25 -0
  68. package/src/derivation/stat-scaling.ts +12 -0
  69. package/src/derivation/stats.ts +6 -6
  70. package/src/derivation/stratum.ts +26 -22
  71. package/src/derivation/tiers.ts +40 -7
  72. package/src/derivation/upgrades.ts +14 -0
  73. package/src/derivation/wormhole.ts +154 -0
  74. package/src/entities/entity.ts +102 -0
  75. package/src/entities/gamestate.ts +3 -28
  76. package/src/entities/makers.ts +141 -135
  77. package/src/entities/slot-multiplier.ts +43 -0
  78. package/src/errors.ts +12 -16
  79. package/src/format.ts +26 -4
  80. package/src/index-module.ts +333 -35
  81. package/src/managers/actions.ts +668 -103
  82. package/src/managers/base.ts +6 -2
  83. package/src/managers/cluster.test.ts +39 -0
  84. package/src/managers/cluster.ts +53 -0
  85. package/src/managers/construction-types.ts +80 -0
  86. package/src/managers/construction.ts +443 -0
  87. package/src/managers/context.ts +29 -1
  88. package/src/managers/coordinates.ts +14 -0
  89. package/src/managers/entities.ts +27 -66
  90. package/src/managers/epochs.ts +40 -0
  91. package/src/managers/index.ts +20 -1
  92. package/src/managers/locations.ts +25 -29
  93. package/src/managers/nft.test.ts +14 -0
  94. package/src/managers/nft.ts +70 -0
  95. package/src/managers/players.ts +25 -0
  96. package/src/managers/plot.ts +122 -0
  97. package/src/nft/atomicassets.abi.json +1342 -0
  98. package/src/nft/atomicassets.ts +237 -0
  99. package/src/nft/atomicdata.ts +130 -0
  100. package/src/nft/buildImmutableData.ts +388 -0
  101. package/src/nft/description.ts +215 -99
  102. package/src/nft/index.ts +3 -0
  103. package/src/planner/index.ts +242 -0
  104. package/src/planner/planner.test.ts +334 -0
  105. package/src/resolution/describe-module.ts +43 -25
  106. package/src/resolution/display-name.ts +38 -10
  107. package/src/resolution/resolve-item.test.ts +48 -0
  108. package/src/resolution/resolve-item.ts +112 -58
  109. package/src/scan/index.ts +244 -0
  110. package/src/scan/scan-wasm.base64.ts +2 -0
  111. package/src/scheduling/accessor.ts +65 -23
  112. package/src/scheduling/availability.test.ts +204 -0
  113. package/src/scheduling/availability.ts +211 -0
  114. package/src/scheduling/cancel.test.ts +542 -0
  115. package/src/scheduling/cancel.ts +254 -0
  116. package/src/scheduling/cluster-stock.test.ts +31 -0
  117. package/src/scheduling/cluster-stock.ts +15 -0
  118. package/src/scheduling/energy.ts +47 -0
  119. package/src/scheduling/idle-resolve.ts +45 -0
  120. package/src/scheduling/jobs.ts +71 -0
  121. package/src/scheduling/lane-core.ts +128 -0
  122. package/src/scheduling/lanes.test.ts +250 -0
  123. package/src/scheduling/lanes.ts +227 -0
  124. package/src/scheduling/projection.ts +257 -133
  125. package/src/scheduling/schedule.test.ts +119 -0
  126. package/src/scheduling/schedule.ts +257 -117
  127. package/src/scheduling/task-cargo.test.ts +44 -0
  128. package/src/scheduling/task-cargo.ts +46 -0
  129. package/src/scheduling/unwrap.test.ts +86 -0
  130. package/src/scheduling/unwrap.ts +190 -0
  131. package/src/shipload.ts +26 -1
  132. package/src/subscriptions/manager.cluster.test.ts +51 -0
  133. package/src/subscriptions/manager.ts +245 -172
  134. package/src/subscriptions/mappers.ts +5 -8
  135. package/src/subscriptions/types.ts +28 -3
  136. package/src/testing/catalog-hash.ts +19 -0
  137. package/src/testing/index.ts +2 -0
  138. package/src/testing/projection-parity.ts +167 -0
  139. package/src/travel/reach.ts +21 -0
  140. package/src/travel/route-planner.ts +288 -0
  141. package/src/travel/route-simulator.ts +170 -0
  142. package/src/travel/travel.ts +188 -122
  143. package/src/types/capabilities.ts +37 -8
  144. package/src/types/entity.ts +3 -3
  145. package/src/types/index.ts +0 -1
  146. package/src/types.ts +51 -16
  147. package/src/utils/cargo.test.ts +14 -0
  148. package/src/utils/cargo.ts +29 -0
  149. package/src/utils/display-name.ts +70 -0
  150. package/src/utils/system.ts +15 -16
  151. package/src/capabilities/loading.ts +0 -8
  152. package/src/entities/container.ts +0 -123
  153. package/src/entities/ship-deploy.ts +0 -295
  154. package/src/entities/ship.ts +0 -221
  155. package/src/entities/warehouse.ts +0 -127
  156. package/src/types/entity-traits.ts +0 -69
@@ -14,7 +14,6 @@ import {
14
14
  type Checksum256,
15
15
  Int64,
16
16
  type Int64Type,
17
- UInt16,
18
17
  UInt32,
19
18
  type UInt32Type,
20
19
  UInt64,
@@ -23,17 +22,29 @@ import {
23
22
 
24
23
  import type {ServerContract} from '../contracts'
25
24
  import {
25
+ BASE_HAUL_PENALTY_MILLI,
26
26
  BASE_ORBITAL_MASS,
27
27
  type CargoMassInfo,
28
28
  type Distance,
29
+ HAULER_EFFICIENCY_DENOM,
29
30
  MAX_ORBITAL_ALTITUDE,
30
31
  MIN_ORBITAL_ALTITUDE,
32
+ MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL,
33
+ MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE,
31
34
  PRECISION,
32
35
  type ShipLike,
33
36
  TaskType,
34
37
  } from '../types'
38
+ import {EntityClass} from '../data/kind-registry'
35
39
  import {getItem} from '../data/catalog'
36
40
  import {hasSystem} from '../utils/system'
41
+ import {WH} from '../derivation/wormhole'
42
+ import * as scheduleModel from '../scheduling/schedule'
43
+ import type {ScheduleData} from '../scheduling/schedule'
44
+
45
+ function isPositionalTask(task: ServerContract.Types.task): boolean {
46
+ return task.type.equals(TaskType.TRAVEL) || task.type.equals(TaskType.TRANSIT)
47
+ }
37
48
 
38
49
  export function calc_orbital_altitude(mass: number): number {
39
50
  if (mass <= BASE_ORBITAL_MASS) {
@@ -111,15 +122,16 @@ export function getInterpolatedPosition(
111
122
  taskIndex: number,
112
123
  taskProgress: number
113
124
  ): FloatPosition {
114
- if (!entity.schedule || entity.schedule.tasks.length === 0) {
125
+ const tasks = mobilityTasks(entity)
126
+ if (tasks.length === 0) {
115
127
  return {x: Number(entity.coordinates.x), y: Number(entity.coordinates.y)}
116
128
  }
117
129
  if (taskIndex < 0) {
118
- const settled = getFlightOrigin(entity, entity.schedule.tasks.length)
130
+ const settled = getFlightOrigin(entity, tasks.length)
119
131
  return {x: Number(settled.x), y: Number(settled.y)}
120
132
  }
121
- const task = entity.schedule.tasks[taskIndex]
122
- if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
133
+ const task = tasks[taskIndex]
134
+ if (!isPositionalTask(task) || !task.coordinates) {
123
135
  const origin = getFlightOrigin(entity, taskIndex)
124
136
  return {x: Number(origin.x), y: Number(origin.y)}
125
137
  }
@@ -176,14 +188,15 @@ export function calc_rechargetime(
176
188
  const cap = UInt32.from(capacity)
177
189
  const eng = UInt32.from(energy)
178
190
  if (eng.gte(cap)) return UInt32.zero
179
- return cap.subtracting(eng).dividing(recharge)
191
+ const ticks = cap.subtracting(eng).dividing(recharge)
192
+ return ticks.equals(UInt32.zero) ? UInt32.from(1) : ticks
180
193
  }
181
194
 
182
195
  export function calc_ship_rechargetime(ship: ShipLike): UInt32 {
183
196
  if (!ship.generator) return UInt32.from(0)
184
197
  return calc_rechargetime(
185
198
  ship.generator.capacity,
186
- ship.energy ?? UInt16.from(0),
199
+ ship.energy ?? UInt32.from(0),
187
200
  ship.generator.recharge
188
201
  )
189
202
  }
@@ -192,20 +205,46 @@ export function calc_flighttime(distance: UInt64Type, acceleration: number): UIn
192
205
  return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
193
206
  }
194
207
 
208
+ export function calc_travel_flighttime(distance: UInt64Type, acceleration: number): UInt32 {
209
+ const cruiseTransitionDistance = 2 * PRECISION
210
+ if (Number(distance) <= cruiseTransitionDistance) {
211
+ return calc_flighttime(distance, acceleration)
212
+ }
213
+
214
+ const cruiseVelocity = Math.sqrt(acceleration * cruiseTransitionDistance)
215
+ return UInt32.from(Number(distance) / cruiseVelocity + cruiseVelocity / acceleration)
216
+ }
217
+
218
+ export function calc_transit_duration(ax: number, ay: number, bx: number, by: number): UInt32 {
219
+ const distance = distanceBetweenPoints(ax, ay, bx, by)
220
+ return UInt32.from(Math.floor(distance.toNumber() / (PRECISION * WH.TRANSIT_SPEED)))
221
+ }
222
+
223
+ // The active entity's chosen loader lane (lowest slot), mirroring cargo.cpp lane selection.
224
+ export function shipLoaderLane(ship: ShipLike): {thrust: number; mass: number} | undefined {
225
+ const lanes = ship.loader_lanes ?? []
226
+ if (lanes.length === 0) return undefined
227
+ let lowest = lanes[0]
228
+ for (const lane of lanes) {
229
+ if (Number(lane.slot_index) < Number(lowest.slot_index)) lowest = lane
230
+ }
231
+ return {thrust: Number(lowest.thrust), mass: Number(lowest.mass)}
232
+ }
233
+
195
234
  export function calc_loader_flighttime(ship: ShipLike, mass: UInt64, altitude?: number): UInt32 {
196
235
  const z = altitude ?? ship.coordinates.z?.toNumber() ?? calc_orbital_altitude(Number(mass))
197
236
  return calc_flighttime(z, calc_loader_acceleration(ship, mass))
198
237
  }
199
238
 
200
239
  export function calc_loader_acceleration(ship: ShipLike, mass: UInt64): number {
201
- const thrust = ship.loaders ? Number(ship.loaders.thrust) : 0
202
- const loaderMass = ship.loaders ? Number(ship.loaders.mass) : 0
203
- return calc_acceleration(thrust, Number(mass) + loaderMass)
240
+ const lane = shipLoaderLane(ship)
241
+ const thrust = lane ? lane.thrust : 0
242
+ return calc_acceleration(thrust, Number(mass))
204
243
  }
205
244
 
206
245
  export function calc_ship_flighttime(ship: ShipLike, mass: UInt64, distance: UInt64): UInt32 {
207
246
  const acceleration = calc_ship_acceleration(ship, mass)
208
- return calc_flighttime(distance, acceleration)
247
+ return calc_travel_flighttime(distance, acceleration)
209
248
  }
210
249
 
211
250
  export function calc_ship_acceleration(ship: ShipLike, mass: UInt64): number {
@@ -222,8 +261,10 @@ export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64
222
261
 
223
262
  mass.add(ship.hullmass)
224
263
 
225
- if (ship.loaders && ship.loaders.quantity.gt(UInt32.zero)) {
226
- mass.add(ship.loaders.mass.multiplying(ship.loaders.quantity))
264
+ if (ship.loader_lanes && ship.loader_lanes.length > 0) {
265
+ for (const l of ship.loader_lanes) {
266
+ mass.add(UInt64.from(l.mass))
267
+ }
227
268
  }
228
269
 
229
270
  for (const cargo of cargos) {
@@ -234,8 +275,31 @@ export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64
234
275
  return mass
235
276
  }
236
277
 
278
+ export function calc_group_flighttime(
279
+ totalThrust: number,
280
+ haulCount: number,
281
+ pooledHaulCap: number,
282
+ weightedHaulEffNum: number,
283
+ totalMass: number,
284
+ distance: UInt64Type
285
+ ): UInt32 {
286
+ const avgHaulEff = pooledHaulCap > 0 ? Math.trunc(weightedHaulEffNum / pooledHaulCap) : 0
287
+ let effectiveThrust = totalThrust
288
+ if (haulCount > 0) {
289
+ const penaltyMilli =
290
+ 1000 +
291
+ Math.trunc(
292
+ (haulCount * BASE_HAUL_PENALTY_MILLI * (HAULER_EFFICIENCY_DENOM - avgHaulEff)) /
293
+ HAULER_EFFICIENCY_DENOM
294
+ )
295
+ effectiveThrust = Math.trunc((totalThrust * 1000) / penaltyMilli)
296
+ }
297
+ const acceleration = calc_acceleration(effectiveThrust, totalMass)
298
+ return calc_travel_flighttime(distance, acceleration)
299
+ }
300
+
237
301
  export function calc_energyusage(distance: UInt64Type, drain: UInt32Type): UInt32 {
238
- return UInt64.from(distance).dividing(PRECISION).multiplying(drain)
302
+ return UInt64.from(distance).multiplying(drain).dividing(PRECISION)
239
303
  }
240
304
 
241
305
  export function calculateTransferTime(
@@ -258,10 +322,10 @@ export function calculateTransferTime(
258
322
  return UInt32.from(0)
259
323
  }
260
324
 
261
- if (!ship.loaders) return UInt32.from(0)
262
- mass = UInt64.from(mass).adding(ship.loaders.mass)
263
- const transfer_time = calc_loader_flighttime(ship, mass)
264
- return transfer_time.dividing(ship.loaders.quantity)
325
+ const lane = shipLoaderLane(ship)
326
+ if (!lane) return UInt32.from(0)
327
+ mass = UInt64.from(mass).adding(UInt64.from(lane.mass))
328
+ return calc_loader_flighttime(ship, mass)
265
329
  }
266
330
 
267
331
  export function calculateRefuelingTime(ship: ShipLike): UInt32 {
@@ -316,25 +380,22 @@ export function calculateLoadTimeBreakdown(
316
380
  let unloadTime = 0
317
381
  let loadTime = 0
318
382
 
319
- if (mass_unload.gt(UInt64.zero) && ship.loaders) {
320
- const totalMass = UInt64.from(mass_unload).adding(ship.loaders.mass)
383
+ const lane = shipLoaderLane(ship)
384
+
385
+ if (mass_unload.gt(UInt64.zero) && lane) {
386
+ const totalMass = UInt64.from(mass_unload).adding(UInt64.from(lane.mass))
321
387
  unloadTime = Number(calc_loader_flighttime(ship, totalMass))
322
388
  }
323
389
 
324
- if (mass_load.gt(UInt64.zero) && ship.loaders) {
325
- const totalMass = UInt64.from(mass_load).adding(ship.loaders.mass)
390
+ if (mass_load.gt(UInt64.zero) && lane) {
391
+ const totalMass = UInt64.from(mass_load).adding(UInt64.from(lane.mass))
326
392
  loadTime = Number(calc_loader_flighttime(ship, totalMass))
327
393
  }
328
394
 
329
- const numLoaders = ship.loaders ? Number(ship.loaders.quantity) : 0
330
- const totalTime = numLoaders > 0 ? (unloadTime + loadTime) / numLoaders : 0
331
- const unloadTimePerLoader = numLoaders > 0 ? unloadTime / numLoaders : 0
332
- const loadTimePerLoader = numLoaders > 0 ? loadTime / numLoaders : 0
333
-
334
395
  return {
335
- unloadTime: unloadTimePerLoader,
336
- loadTime: loadTimePerLoader,
337
- totalTime,
396
+ unloadTime,
397
+ loadTime,
398
+ totalTime: unloadTime + loadTime,
338
399
  unloadMass: Number(mass_unload),
339
400
  loadMass: Number(mass_load),
340
401
  }
@@ -368,24 +429,16 @@ export function estimateTravelTime(
368
429
  let loadTime = UInt32.zero
369
430
  let unloadTime = UInt32.zero
370
431
 
371
- if (
372
- loadMass &&
373
- UInt32.from(loadMass).gt(UInt32.zero) &&
374
- ship.loaders &&
375
- ship.loaders.quantity.gt(UInt32.zero)
376
- ) {
377
- const totalMass = UInt64.from(loadMass).adding(ship.loaders.mass)
378
- loadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
432
+ const lane = shipLoaderLane(ship)
433
+
434
+ if (loadMass && UInt32.from(loadMass).gt(UInt32.zero) && lane) {
435
+ const totalMass = UInt64.from(loadMass).adding(UInt64.from(lane.mass))
436
+ loadTime = calc_loader_flighttime(ship, totalMass)
379
437
  }
380
438
 
381
- if (
382
- unloadMass &&
383
- UInt32.from(unloadMass).gt(UInt32.zero) &&
384
- ship.loaders &&
385
- ship.loaders.quantity.gt(UInt32.zero)
386
- ) {
387
- const totalMass = UInt64.from(unloadMass).adding(ship.loaders.mass)
388
- unloadTime = calc_loader_flighttime(ship, totalMass).dividing(ship.loaders.quantity)
439
+ if (unloadMass && UInt32.from(unloadMass).gt(UInt32.zero) && lane) {
440
+ const totalMass = UInt64.from(unloadMass).adding(UInt64.from(lane.mass))
441
+ unloadTime = calc_loader_flighttime(ship, totalMass)
389
442
  }
390
443
 
391
444
  return {
@@ -417,30 +470,51 @@ export function hasEnergyForDistance(ship: ShipLike, distance: UInt64Type): bool
417
470
  return UInt64.from(ship.energy ?? 0).gte(energyNeeded)
418
471
  }
419
472
 
473
+ export interface TransferLoaderLane {
474
+ slot_index?: {toNumber(): number} | number
475
+ thrust: {toNumber(): number} | number
476
+ mass: {toNumber(): number} | number
477
+ }
478
+
420
479
  export interface TransferEntity {
421
480
  location: {z?: {toNumber(): number} | number}
422
- loaders?: {
423
- thrust: {toNumber(): number} | number
424
- mass: {toNumber(): number} | number
425
- quantity: {toNumber(): number} | number
481
+ entityClass: EntityClass
482
+ loaderLanes?: TransferLoaderLane[]
483
+ }
484
+
485
+ function toNum(v: {toNumber(): number} | number | undefined): number {
486
+ if (v === undefined) return 0
487
+ return typeof v === 'number' ? v : v.toNumber()
488
+ }
489
+
490
+ // Mirrors cargo.cpp worker_lane_key_or_mobility: lowest-slot loader lane (display has no busy context).
491
+ function chosenLoaderLane(entity: TransferEntity): TransferLoaderLane | undefined {
492
+ const lanes = entity.loaderLanes ?? []
493
+ if (lanes.length === 0) return undefined
494
+ let lowest = lanes[0]
495
+ for (const lane of lanes) {
496
+ if (toNum(lane.slot_index) < toNum(lowest.slot_index)) lowest = lane
426
497
  }
498
+ return lowest
427
499
  }
428
500
 
429
- export interface HasScheduleAndLocation {
501
+ export interface HasScheduleAndLocation extends ScheduleData {
430
502
  coordinates: ServerContract.ActionParams.Type.coordinates
431
- schedule?: ServerContract.Types.schedule
503
+ }
504
+
505
+ function mobilityTasks(entity: HasScheduleAndLocation): ServerContract.Types.task[] {
506
+ return scheduleModel.mobilityLane(entity)?.schedule.tasks ?? []
432
507
  }
433
508
 
434
509
  export function getFlightOrigin(
435
510
  entity: HasScheduleAndLocation,
436
511
  flightTaskIndex: number
437
512
  ): ServerContract.ActionParams.Type.coordinates {
438
- if (!entity.schedule) return entity.coordinates
439
-
513
+ const tasks = mobilityTasks(entity)
440
514
  let origin = entity.coordinates
441
- for (let i = 0; i < flightTaskIndex && i < entity.schedule.tasks.length; i++) {
442
- const task = entity.schedule.tasks[i]
443
- if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
515
+ for (let i = 0; i < flightTaskIndex && i < tasks.length; i++) {
516
+ const task = tasks[i]
517
+ if (isPositionalTask(task) && task.coordinates) {
444
518
  origin = task.coordinates
445
519
  }
446
520
  }
@@ -450,11 +524,10 @@ export function getFlightOrigin(
450
524
  export function getDestinationLocation(
451
525
  entity: HasScheduleAndLocation
452
526
  ): ServerContract.ActionParams.Type.coordinates | undefined {
453
- if (!entity.schedule) return undefined
454
-
455
- for (let i = entity.schedule.tasks.length - 1; i >= 0; i--) {
456
- const task = entity.schedule.tasks[i]
457
- if (task.type.equals(TaskType.TRAVEL) && task.coordinates) {
527
+ const tasks = mobilityTasks(entity)
528
+ for (let i = tasks.length - 1; i >= 0; i--) {
529
+ const task = tasks[i]
530
+ if (isPositionalTask(task) && task.coordinates) {
458
531
  return task.coordinates
459
532
  }
460
533
  }
@@ -467,16 +540,17 @@ export function getPositionAt(
467
540
  taskIndex: number,
468
541
  taskProgress: number
469
542
  ): ServerContract.ActionParams.Type.coordinates {
470
- if (!entity.schedule || entity.schedule.tasks.length === 0) {
543
+ const tasks = mobilityTasks(entity)
544
+ if (tasks.length === 0) {
471
545
  return entity.coordinates
472
546
  }
473
547
  if (taskIndex < 0) {
474
- return getFlightOrigin(entity, entity.schedule.tasks.length)
548
+ return getFlightOrigin(entity, tasks.length)
475
549
  }
476
550
 
477
- const task = entity.schedule.tasks[taskIndex]
551
+ const task = tasks[taskIndex]
478
552
 
479
- if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
553
+ if (!isPositionalTask(task) || !task.coordinates) {
480
554
  return getFlightOrigin(entity, taskIndex)
481
555
  }
482
556
 
@@ -490,68 +564,60 @@ export function getPositionAt(
490
564
  }
491
565
  }
492
566
 
493
- export function calc_transfer_duration(
494
- source: TransferEntity,
495
- dest: TransferEntity,
567
+ export function minTransferDistance(entityClass: EntityClass): number {
568
+ return entityClass === EntityClass.OrbitalVessel
569
+ ? MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL
570
+ : MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE
571
+ }
572
+
573
+ // Mirrors cargo.cpp calc_onesided_duration: single active loader's thrust + mass, no ÷quantity.
574
+ export function calc_onesided_duration(
575
+ loaderThrust: number,
576
+ loaderMass: number,
577
+ activeZ: number,
578
+ counterpartZ: number,
579
+ activeEntityClass: EntityClass,
580
+ counterpartEntityClass: EntityClass,
496
581
  cargoMass: number
497
582
  ): number {
498
- if (cargoMass === 0) {
583
+ if (cargoMass === 0 || loaderThrust === 0) {
499
584
  return 0
500
585
  }
586
+ const rawDistance = Math.abs(activeZ - counterpartZ)
587
+ const minDistance = Math.max(
588
+ minTransferDistance(activeEntityClass),
589
+ minTransferDistance(counterpartEntityClass)
590
+ )
591
+ const distance = rawDistance < minDistance ? minDistance : rawDistance
592
+ const totalMass = cargoMass + loaderMass
593
+ const acceleration = calc_acceleration(loaderThrust, totalMass)
594
+ const flightTime = Math.floor(2 * Math.sqrt(distance / acceleration))
595
+ return flightTime === 0 ? 1 : flightTime
596
+ }
501
597
 
502
- let totalThrust = 0
503
- let totalLoaderMass = 0
504
- let totalQuantity = 0
505
-
506
- if (source.loaders) {
507
- const thrust =
508
- typeof source.loaders.thrust === 'number'
509
- ? source.loaders.thrust
510
- : source.loaders.thrust.toNumber()
511
- const mass =
512
- typeof source.loaders.mass === 'number'
513
- ? source.loaders.mass
514
- : source.loaders.mass.toNumber()
515
- const qty =
516
- typeof source.loaders.quantity === 'number'
517
- ? source.loaders.quantity
518
- : source.loaders.quantity.toNumber()
519
- totalThrust += thrust * qty
520
- totalLoaderMass += mass * qty
521
- totalQuantity += qty
522
- }
523
-
524
- if (dest.loaders) {
525
- const thrust =
526
- typeof dest.loaders.thrust === 'number'
527
- ? dest.loaders.thrust
528
- : dest.loaders.thrust.toNumber()
529
- const mass =
530
- typeof dest.loaders.mass === 'number' ? dest.loaders.mass : dest.loaders.mass.toNumber()
531
- const qty =
532
- typeof dest.loaders.quantity === 'number'
533
- ? dest.loaders.quantity
534
- : dest.loaders.quantity.toNumber()
535
- totalThrust += thrust * qty
536
- totalLoaderMass += mass * qty
537
- totalQuantity += qty
538
- }
539
-
540
- if (totalThrust === 0 || totalQuantity === 0) {
598
+ // Mirrors cargo.cpp: the active (loader-bearing) entity's chosen loader lane drives the duration.
599
+ export function calc_transfer_duration(
600
+ source: TransferEntity,
601
+ dest: TransferEntity,
602
+ cargoMass: number
603
+ ): number {
604
+ const active = chosenLoaderLane(source) ? source : dest
605
+ const counterpart = active === source ? dest : source
606
+ const lane = chosenLoaderLane(active)
607
+ if (!lane) {
541
608
  return 0
542
609
  }
543
610
 
544
- const sourceZ =
545
- typeof source.location.z === 'number'
546
- ? source.location.z
547
- : (source.location.z?.toNumber() ?? 0)
548
- const destZ =
549
- typeof dest.location.z === 'number' ? dest.location.z : (dest.location.z?.toNumber() ?? 0)
550
- const distance = Math.abs(sourceZ - destZ)
551
-
552
- const totalMass = cargoMass + totalLoaderMass
553
- const acceleration = calc_acceleration(totalThrust, totalMass)
554
- const flightTime = 2 * Math.sqrt(distance / acceleration)
555
-
556
- return Math.floor(flightTime / totalQuantity)
611
+ const activeZ = toNum(active.location.z)
612
+ const counterpartZ = toNum(counterpart.location.z)
613
+
614
+ return calc_onesided_duration(
615
+ toNum(lane.thrust),
616
+ toNum(lane.mass),
617
+ activeZ,
618
+ counterpartZ,
619
+ active.entityClass,
620
+ counterpart.entityClass,
621
+ cargoMass
622
+ )
557
623
  }
@@ -1,13 +1,35 @@
1
- import type {Name, UInt16, UInt32} from '@wharfkit/antelope'
1
+ import type {Name, UInt32, UInt8} from '@wharfkit/antelope'
2
2
  import type {ServerContract} from '../contracts'
3
3
 
4
+ export interface LoaderStats {
5
+ mass: {toNumber(): number; multiplying(v: unknown): {toNumber(): number}}
6
+ thrust: {toNumber(): number}
7
+ quantity: {toNumber(): number; gt(v: unknown): boolean}
8
+ }
9
+
10
+ export interface GathererStats {
11
+ yield: {toNumber(): number}
12
+ drain: {toNumber(): number}
13
+ depth: {toNumber(): number; toString(): string}
14
+ }
15
+
16
+ export interface CrafterStats {
17
+ speed: {toNumber(): number}
18
+ drain: {toNumber(): number}
19
+ }
20
+
21
+ export interface BuilderStats {
22
+ speed: {toNumber(): number}
23
+ drain: {toNumber(): number}
24
+ }
25
+
4
26
  export interface MovementCapability {
5
27
  engines: ServerContract.Types.movement_stats
6
28
  generator: ServerContract.Types.energy_stats
7
29
  }
8
30
 
9
31
  export interface EnergyCapability {
10
- energy: UInt16
32
+ energy: UInt32
11
33
  }
12
34
 
13
35
  export interface StorageCapability {
@@ -17,11 +39,11 @@ export interface StorageCapability {
17
39
  }
18
40
 
19
41
  export interface LoaderCapability {
20
- loaders: ServerContract.Types.loader_stats
42
+ loaders: LoaderStats
21
43
  }
22
44
 
23
45
  export interface GathererCapability {
24
- gatherer: ServerContract.Types.gatherer_stats
46
+ gatherer: GathererStats
25
47
  }
26
48
 
27
49
  export interface MassCapability {
@@ -29,6 +51,7 @@ export interface MassCapability {
29
51
  }
30
52
 
31
53
  export interface ScheduleCapability {
54
+ lanes?: ServerContract.Types.lane[]
32
55
  schedule?: ServerContract.Types.schedule
33
56
  }
34
57
 
@@ -37,16 +60,18 @@ export interface EntityCapabilities {
37
60
  capacity?: UInt32
38
61
  engines?: ServerContract.Types.movement_stats
39
62
  generator?: ServerContract.Types.energy_stats
40
- loaders?: ServerContract.Types.loader_stats
41
- gatherer?: ServerContract.Types.gatherer_stats
42
- crafter?: ServerContract.Types.crafter_stats
63
+ loaders?: LoaderStats
64
+ gatherer?: GathererStats
65
+ crafter?: CrafterStats
66
+ builder?: BuilderStats
43
67
  hauler?: ServerContract.Types.hauler_stats
68
+ launcher?: ServerContract.Types.launcher_stats
44
69
  }
45
70
 
46
71
  export interface EntityState {
47
72
  owner: Name
48
73
  location: ServerContract.Types.coordinates
49
- energy?: UInt16
74
+ energy?: UInt32
50
75
  cargomass: UInt32
51
76
  cargo: ServerContract.Types.cargo_item[]
52
77
  }
@@ -74,3 +99,7 @@ export function capsHasMass(caps: EntityCapabilities): boolean {
74
99
  export function capsHasHauler(caps: EntityCapabilities): boolean {
75
100
  return caps.hauler !== undefined
76
101
  }
102
+
103
+ export function capsHasLauncher(caps: EntityCapabilities): boolean {
104
+ return caps.launcher !== undefined
105
+ }
@@ -3,6 +3,7 @@ import type {ServerContract} from '../contracts'
3
3
  import type {Coordinates} from '../types'
4
4
  import type {
5
5
  EnergyCapability,
6
+ GathererCapability,
6
7
  LoaderCapability,
7
8
  MassCapability,
8
9
  MovementCapability,
@@ -24,9 +25,8 @@ export type ShipEntity = Entity &
24
25
  StorageCapability &
25
26
  Partial<LoaderCapability> &
26
27
  MassCapability &
27
- ScheduleCapability & {
28
- gatherer?: ServerContract.Types.gatherer_stats
29
- }
28
+ ScheduleCapability &
29
+ Partial<GathererCapability>
30
30
 
31
31
  export type WarehouseEntity = Entity &
32
32
  StorageCapability &
@@ -1,3 +1,2 @@
1
1
  export * from './capabilities'
2
2
  export * from './entity'
3
- export * from './entity-traits'