@shipload/sdk 1.0.0-next.1 → 1.0.0-next.11

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 (56) hide show
  1. package/lib/shipload.d.ts +618 -353
  2. package/lib/shipload.js +2250 -1059
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +2193 -1044
  5. package/lib/shipload.m.js.map +1 -1
  6. package/package.json +2 -2
  7. package/src/capabilities/modules.ts +3 -0
  8. package/src/capabilities/storage.ts +1 -1
  9. package/src/contracts/platform.ts +13 -1
  10. package/src/contracts/server.ts +227 -282
  11. package/src/data/capabilities.ts +5 -330
  12. package/src/data/capability-formulas.ts +70 -0
  13. package/src/data/catalog.ts +0 -5
  14. package/src/data/colors.ts +2 -16
  15. package/src/data/entities.json +46 -10
  16. package/src/data/item-ids.ts +4 -1
  17. package/src/data/items.json +264 -0
  18. package/src/data/metadata.ts +63 -1
  19. package/src/data/recipes-runtime.ts +1 -0
  20. package/src/data/recipes.json +139 -11
  21. package/src/derivation/capability-mappings.ts +122 -0
  22. package/src/derivation/index.ts +1 -0
  23. package/src/derivation/resources.ts +116 -37
  24. package/src/derivation/stats.ts +1 -2
  25. package/src/entities/container.ts +25 -10
  26. package/src/entities/extractor.ts +144 -0
  27. package/src/entities/factory.ts +135 -0
  28. package/src/entities/gamestate.ts +0 -9
  29. package/src/entities/makers.ts +130 -20
  30. package/src/entities/nexus.ts +29 -0
  31. package/src/entities/ship-deploy.ts +114 -56
  32. package/src/entities/ship.ts +17 -0
  33. package/src/entities/slot-multiplier.ts +21 -0
  34. package/src/entities/warehouse.ts +20 -3
  35. package/src/errors.ts +10 -13
  36. package/src/index-module.ts +81 -26
  37. package/src/managers/actions.ts +53 -80
  38. package/src/managers/entities.ts +65 -17
  39. package/src/managers/locations.ts +2 -20
  40. package/src/nft/atomicdata.ts +128 -0
  41. package/src/nft/description.ts +41 -7
  42. package/src/nft/index.ts +1 -0
  43. package/src/resolution/resolve-item.ts +17 -9
  44. package/src/scheduling/accessor.ts +4 -0
  45. package/src/scheduling/projection.ts +10 -2
  46. package/src/scheduling/schedule.ts +15 -1
  47. package/src/scheduling/task-cargo.ts +47 -0
  48. package/src/subscriptions/connection.ts +50 -2
  49. package/src/subscriptions/manager.ts +84 -4
  50. package/src/subscriptions/mappers.ts +5 -1
  51. package/src/subscriptions/types.ts +2 -2
  52. package/src/travel/travel.ts +61 -2
  53. package/src/types/entity-traits.ts +103 -1
  54. package/src/types.ts +11 -1
  55. package/src/utils/cargo.ts +27 -0
  56. package/src/utils/system.ts +25 -24
@@ -7,6 +7,9 @@ export interface WebSocketConnectionOptions {
7
7
  url: string
8
8
  onMessage: (message: ServerMessage) => void
9
9
  onStateChange?: (state: ConnectionState) => void
10
+ minReconnectDelay?: number
11
+ pingIntervalMs?: number
12
+ pongTimeoutMs?: number
10
13
  }
11
14
 
12
15
  export class WebSocketConnection {
@@ -19,15 +22,26 @@ export class WebSocketConnection {
19
22
  private _state: ConnectionState = 'disconnected'
20
23
  private shouldReconnect = true
21
24
  private sendQueue: string[] = []
25
+ private minReconnectDelay: number
26
+ private pingIntervalMs: number
27
+ private pongTimeoutMs: number
28
+ private pingTimer: ReturnType<typeof setInterval> | null = null
29
+ private staleTimer: ReturnType<typeof setTimeout> | null = null
22
30
 
23
- private static readonly MIN_RECONNECT_DELAY = 1000
31
+ private static readonly DEFAULT_MIN_RECONNECT_DELAY = 1000
24
32
  private static readonly MAX_RECONNECT_DELAY = 30000
25
33
  private static readonly RECONNECT_MULTIPLIER = 2
34
+ private static readonly DEFAULT_PING_INTERVAL_MS = 25000
35
+ private static readonly DEFAULT_PONG_TIMEOUT_MS = 10000
26
36
 
27
37
  constructor(options: WebSocketConnectionOptions) {
28
38
  this.url = options.url
29
39
  this.onMessage = options.onMessage
30
40
  this.onStateChange = options.onStateChange
41
+ this.minReconnectDelay =
42
+ options.minReconnectDelay ?? WebSocketConnection.DEFAULT_MIN_RECONNECT_DELAY
43
+ this.pingIntervalMs = options.pingIntervalMs ?? WebSocketConnection.DEFAULT_PING_INTERVAL_MS
44
+ this.pongTimeoutMs = options.pongTimeoutMs ?? WebSocketConnection.DEFAULT_PONG_TIMEOUT_MS
31
45
  }
32
46
 
33
47
  get state(): ConnectionState {
@@ -64,9 +78,11 @@ export class WebSocketConnection {
64
78
  ) {
65
79
  this.ws.send(this.sendQueue.shift()!)
66
80
  }
81
+ this.startHeartbeat()
67
82
  }
68
83
 
69
84
  this.ws.onmessage = (event) => {
85
+ this.resetStaleTimer()
70
86
  try {
71
87
  const message = JSON.parse(event.data) as ServerMessage
72
88
  this.onMessage(message)
@@ -77,6 +93,7 @@ export class WebSocketConnection {
77
93
  }
78
94
 
79
95
  this.ws.onclose = () => {
96
+ this.stopHeartbeat()
80
97
  this.ws = null
81
98
  this.sendQueue.length = 0
82
99
 
@@ -104,7 +121,7 @@ export class WebSocketConnection {
104
121
  }
105
122
 
106
123
  const delay = Math.min(
107
- WebSocketConnection.MIN_RECONNECT_DELAY *
124
+ this.minReconnectDelay *
108
125
  WebSocketConnection.RECONNECT_MULTIPLIER ** this.reconnectAttempts,
109
126
  WebSocketConnection.MAX_RECONNECT_DELAY
110
127
  )
@@ -126,6 +143,8 @@ export class WebSocketConnection {
126
143
  this.reconnectTimeout = null
127
144
  }
128
145
 
146
+ this.stopHeartbeat()
147
+
129
148
  if (this.ws) {
130
149
  this.ws.close()
131
150
  this.ws = null
@@ -135,6 +154,35 @@ export class WebSocketConnection {
135
154
  this.setState('disconnected')
136
155
  }
137
156
 
157
+ private startHeartbeat() {
158
+ this.stopHeartbeat()
159
+ this.resetStaleTimer()
160
+ this.pingTimer = setInterval(() => {
161
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
162
+ this.ws.send(JSON.stringify({type: 'ping'}))
163
+ }
164
+ }, this.pingIntervalMs)
165
+ }
166
+
167
+ private stopHeartbeat() {
168
+ if (this.pingTimer) {
169
+ clearInterval(this.pingTimer)
170
+ this.pingTimer = null
171
+ }
172
+ if (this.staleTimer) {
173
+ clearTimeout(this.staleTimer)
174
+ this.staleTimer = null
175
+ }
176
+ }
177
+
178
+ private resetStaleTimer() {
179
+ if (this.staleTimer) clearTimeout(this.staleTimer)
180
+ this.staleTimer = setTimeout(() => {
181
+ debug('No frames within ping interval + pong timeout — forcing reconnect')
182
+ if (this.ws) this.ws.close()
183
+ }, this.pingIntervalMs + this.pongTimeoutMs)
184
+ }
185
+
138
186
  close() {
139
187
  this.disconnect()
140
188
  }
@@ -1,4 +1,4 @@
1
- import {WebSocketConnection} from './connection'
1
+ import {WebSocketConnection, type ConnectionState} from './connection'
2
2
  import type {
3
3
  BoundingBox,
4
4
  BoundsDeltaMessage,
@@ -16,12 +16,16 @@ import {mapEntity, parseWireEntity} from './mappers'
16
16
  import type {Ship} from '../entities/ship'
17
17
  import type {Warehouse} from '../entities/warehouse'
18
18
  import type {Container} from '../entities/container'
19
+ import type {Nexus} from '../entities/nexus'
19
20
 
20
- export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container'
21
- export type EntityInstance = Ship | Warehouse | Container
21
+ export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container' | 'nexus'
22
+ export type EntityInstance = Ship | Warehouse | Container | Nexus
22
23
 
23
24
  export interface SubscriptionsOptions {
24
25
  url: string
26
+ minReconnectDelay?: number
27
+ pingIntervalMs?: number
28
+ pongTimeoutMs?: number
25
29
  }
26
30
 
27
31
  export interface BoundsSubscriptionHandle {
@@ -31,6 +35,12 @@ export interface BoundsSubscriptionHandle {
31
35
  current: Map<number, EntityInstance>
32
36
  }
33
37
 
38
+ export interface OwnerSubscriptionHandle {
39
+ readonly subId: string
40
+ unsubscribe(): void
41
+ current: Map<number, EntityInstance>
42
+ }
43
+
34
44
  export interface EntitySubscriptionHandle {
35
45
  readonly subId: string
36
46
  readonly entityType: SubscriptionEntityType
@@ -53,18 +63,26 @@ export class SubscriptionsManager {
53
63
  private readonly boundsSubs = new Map<
54
64
  string,
55
65
  {
66
+ bounds?: BoundingBox
67
+ owner?: string
68
+ prioritizeOwner?: string
56
69
  onSnapshot?: (entities: EntityInstance[]) => void
57
70
  onUpdate?: (entity: EntityInstance) => void
58
71
  onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
59
- handle: BoundsSubscriptionHandle
72
+ handle: BoundsSubscriptionHandle | OwnerSubscriptionHandle
60
73
  }
61
74
  >()
62
75
  private subCounter = 0
76
+ private hasConnected = false
63
77
 
64
78
  constructor(opts: SubscriptionsOptions) {
65
79
  this.conn = new WebSocketConnection({
66
80
  url: opts.url,
67
81
  onMessage: (m) => this.onMessage(m),
82
+ onStateChange: (s) => this.onStateChange(s),
83
+ minReconnectDelay: opts.minReconnectDelay,
84
+ pingIntervalMs: opts.pingIntervalMs,
85
+ pongTimeoutMs: opts.pongTimeoutMs,
68
86
  })
69
87
  this.conn.connect()
70
88
  }
@@ -139,6 +157,9 @@ export class SubscriptionsManager {
139
157
  current: new Map(),
140
158
  }
141
159
  this.boundsSubs.set(subId, {
160
+ bounds,
161
+ owner: handlers.owner,
162
+ prioritizeOwner: handlers.prioritizeOwner,
142
163
  onSnapshot: handlers.onSnapshot,
143
164
  onUpdate: handlers.onUpdate,
144
165
  onBoundsDelta: handlers.onBoundsDelta,
@@ -148,16 +169,75 @@ export class SubscriptionsManager {
148
169
  return handle
149
170
  }
150
171
 
172
+ subscribeOwner(
173
+ owner: string,
174
+ handlers: {
175
+ onSnapshot?: (entities: EntityInstance[]) => void
176
+ onUpdate?: (entity: EntityInstance) => void
177
+ } = {}
178
+ ): OwnerSubscriptionHandle {
179
+ const subId = this.generateSubID('own')
180
+ const msg: SubscribeMessage = {
181
+ type: 'subscribe',
182
+ sub_id: subId,
183
+ owner,
184
+ }
185
+ const handle: OwnerSubscriptionHandle = {
186
+ subId,
187
+ unsubscribe: () => this.unsubscribeBounds(subId),
188
+ current: new Map(),
189
+ }
190
+ this.boundsSubs.set(subId, {
191
+ bounds: undefined,
192
+ owner,
193
+ prioritizeOwner: undefined,
194
+ onSnapshot: handlers.onSnapshot,
195
+ onUpdate: handlers.onUpdate,
196
+ handle,
197
+ })
198
+ this.sendMessage(msg)
199
+ return handle
200
+ }
201
+
151
202
  private unsubscribeBounds(subId: string) {
152
203
  this.boundsSubs.delete(subId)
153
204
  this.sendMessage({type: 'unsubscribe', sub_id: subId})
154
205
  }
155
206
 
156
207
  private updateBounds(subId: string, bounds: BoundingBox) {
208
+ const entry = this.boundsSubs.get(subId)
209
+ if (entry) entry.bounds = bounds
157
210
  const msg: UpdateBoundsMessage = {type: 'update_bounds', sub_id: subId, bounds}
158
211
  this.sendMessage(msg)
159
212
  }
160
213
 
214
+ private onStateChange(state: ConnectionState) {
215
+ if (state !== 'connected') return
216
+ if (!this.hasConnected) {
217
+ this.hasConnected = true
218
+ return
219
+ }
220
+ for (const [subId, entry] of this.entitySubs) {
221
+ const msg: SubscribeEntityMessage = {
222
+ type: 'subscribe_entity',
223
+ sub_id: subId,
224
+ entity_type: entry.type,
225
+ entity_id: entry.id,
226
+ }
227
+ this.sendMessage(msg)
228
+ }
229
+ for (const [subId, entry] of this.boundsSubs) {
230
+ const msg: SubscribeMessage = {
231
+ type: 'subscribe',
232
+ sub_id: subId,
233
+ bounds: entry.bounds,
234
+ owner: entry.owner,
235
+ prioritize_owner: entry.prioritizeOwner,
236
+ }
237
+ this.sendMessage(msg)
238
+ }
239
+ }
240
+
161
241
  private onMessage(msg: ServerMessage) {
162
242
  switch (msg.type) {
163
243
  case 'snapshot':
@@ -2,12 +2,16 @@ import {ServerContract} from '../contracts'
2
2
  import {Ship} from '../entities/ship'
3
3
  import {Warehouse} from '../entities/warehouse'
4
4
  import {Container} from '../entities/container'
5
+ import {Nexus} from '../entities/nexus'
5
6
  import type {WireEntity} from './types'
6
7
 
7
- export function mapEntity(ei: ServerContract.Types.entity_info): Ship | Warehouse | Container {
8
+ export function mapEntity(
9
+ ei: ServerContract.Types.entity_info
10
+ ): Ship | Warehouse | Container | Nexus {
8
11
  if (ei.type.equals('ship')) return new Ship(ei)
9
12
  if (ei.type.equals('warehouse')) return new Warehouse(ei)
10
13
  if (ei.type.equals('container')) return new Container(ei)
14
+ if (ei.type.equals('nexus')) return new Nexus(ei)
11
15
  throw new Error(`mapEntity: unknown entity type ${ei.type.toString()}`)
12
16
  }
13
17
 
@@ -39,7 +39,7 @@ export type UnsubscribeMessage = {
39
39
  export type SubscribeEntityMessage = {
40
40
  type: 'subscribe_entity'
41
41
  sub_id: string
42
- entity_type: 'ship' | 'warehouse' | 'container'
42
+ entity_type: 'ship' | 'warehouse' | 'container' | 'nexus'
43
43
  entity_id: string
44
44
  }
45
45
 
@@ -80,7 +80,7 @@ export type AckMessage = {
80
80
 
81
81
  export type WireEntity = Record<string, unknown> & {
82
82
  type: number
83
- type_name: 'ship' | 'warehouse' | 'container'
83
+ type_name: 'ship' | 'warehouse' | 'container' | 'nexus'
84
84
  id: string | number
85
85
  owner: string
86
86
  coordinates: WireCoordinates
@@ -28,6 +28,7 @@ import {
28
28
  type Distance,
29
29
  MAX_ORBITAL_ALTITUDE,
30
30
  MIN_ORBITAL_ALTITUDE,
31
+ MIN_TRANSFER_DISTANCE,
31
32
  PRECISION,
32
33
  type ShipLike,
33
34
  TaskType,
@@ -77,6 +78,59 @@ export function lerp(
77
78
  }
78
79
  }
79
80
 
81
+ export interface FloatPosition {
82
+ x: number
83
+ y: number
84
+ }
85
+
86
+ export function easeFlightProgress(t: number): number {
87
+ if (t <= 0) return 0
88
+ if (t >= 1) return 1
89
+ return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t)
90
+ }
91
+
92
+ export function flightSpeedFactor(t: number): number {
93
+ if (t <= 0 || t >= 1) return 0
94
+ return t < 0.5 ? 4 * t : 4 * (1 - t)
95
+ }
96
+
97
+ export function interpolateFlightPosition(
98
+ origin: {x: Int64Type | number; y: Int64Type | number},
99
+ destination: {x: Int64Type | number; y: Int64Type | number},
100
+ taskProgress: number,
101
+ options?: {easing?: 'physics' | 'linear'}
102
+ ): FloatPosition {
103
+ const t = options?.easing === 'linear' ? taskProgress : easeFlightProgress(taskProgress)
104
+ return {
105
+ x: (1 - t) * Number(origin.x) + t * Number(destination.x),
106
+ y: (1 - t) * Number(origin.y) + t * Number(destination.y),
107
+ }
108
+ }
109
+
110
+ export function getInterpolatedPosition(
111
+ entity: HasScheduleAndLocation,
112
+ taskIndex: number,
113
+ taskProgress: number
114
+ ): FloatPosition {
115
+ if (!entity.schedule || entity.schedule.tasks.length === 0) {
116
+ return {x: Number(entity.coordinates.x), y: Number(entity.coordinates.y)}
117
+ }
118
+ if (taskIndex < 0) {
119
+ const settled = getFlightOrigin(entity, entity.schedule.tasks.length)
120
+ return {x: Number(settled.x), y: Number(settled.y)}
121
+ }
122
+ const task = entity.schedule.tasks[taskIndex]
123
+ if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
124
+ const origin = getFlightOrigin(entity, taskIndex)
125
+ return {x: Number(origin.x), y: Number(origin.y)}
126
+ }
127
+ return interpolateFlightPosition(
128
+ getFlightOrigin(entity, taskIndex),
129
+ task.coordinates,
130
+ taskProgress
131
+ )
132
+ }
133
+
80
134
  export function rotation(
81
135
  origin: ServerContract.ActionParams.Type.coordinates,
82
136
  destination: ServerContract.ActionParams.Type.coordinates
@@ -408,14 +462,18 @@ export function getDestinationLocation(
408
462
  return undefined
409
463
  }
410
464
 
465
+ /** Returns chain-tile coordinates (rounded). For visual position use getInterpolatedPosition. */
411
466
  export function getPositionAt(
412
467
  entity: HasScheduleAndLocation,
413
468
  taskIndex: number,
414
469
  taskProgress: number
415
470
  ): ServerContract.ActionParams.Type.coordinates {
416
- if (!entity.schedule || entity.schedule.tasks.length === 0 || taskIndex < 0) {
471
+ if (!entity.schedule || entity.schedule.tasks.length === 0) {
417
472
  return entity.coordinates
418
473
  }
474
+ if (taskIndex < 0) {
475
+ return getFlightOrigin(entity, entity.schedule.tasks.length)
476
+ }
419
477
 
420
478
  const task = entity.schedule.tasks[taskIndex]
421
479
 
@@ -490,7 +548,8 @@ export function calc_transfer_duration(
490
548
  : (source.location.z?.toNumber() ?? 0)
491
549
  const destZ =
492
550
  typeof dest.location.z === 'number' ? dest.location.z : (dest.location.z?.toNumber() ?? 0)
493
- const distance = Math.abs(sourceZ - destZ)
551
+ const rawDistance = Math.abs(sourceZ - destZ)
552
+ const distance = rawDistance < MIN_TRANSFER_DISTANCE ? MIN_TRANSFER_DISTANCE : rawDistance
494
553
 
495
554
  const totalMass = cargoMass + totalLoaderMass
496
555
  const acceleration = calc_acceleration(totalThrust, totalMass)
@@ -1,16 +1,67 @@
1
1
  import {Name} from '@wharfkit/antelope'
2
+ import {
3
+ ITEM_CONTAINER_T1_PACKED,
4
+ ITEM_CONTAINER_T2_PACKED,
5
+ ITEM_EXTRACTOR_T1_PACKED,
6
+ ITEM_FACTORY_T1_PACKED,
7
+ ITEM_SHIP_T1_PACKED,
8
+ ITEM_WAREHOUSE_T1_PACKED,
9
+ } from '../data/item-ids'
2
10
 
3
11
  export const ENTITY_SHIP = Name.from('ship')
4
12
  export const ENTITY_WAREHOUSE = Name.from('warehouse')
13
+ export const ENTITY_EXTRACTOR = Name.from('extractor')
14
+ export const ENTITY_FACTORY = Name.from('factory')
5
15
  export const ENTITY_CONTAINER = Name.from('container')
16
+ export const ENTITY_NEXUS = Name.from('nexus')
6
17
 
7
- export type EntityTypeName = 'ship' | 'warehouse' | 'container'
18
+ export enum EntityClass {
19
+ OrbitalVessel = 0,
20
+ PlanetaryStructure = 1,
21
+ }
22
+
23
+ export function getEntityClass(entityType: Name | EntityTypeName): EntityClass {
24
+ const typeName = typeof entityType === 'string' ? entityType : entityType.toString()
25
+ switch (typeName) {
26
+ case 'ship':
27
+ case 'container':
28
+ case 'nexus':
29
+ return EntityClass.OrbitalVessel
30
+ case 'warehouse':
31
+ case 'extractor':
32
+ case 'factory':
33
+ return EntityClass.PlanetaryStructure
34
+ default:
35
+ throw new Error(`Entity type has no class: ${typeName}`)
36
+ }
37
+ }
38
+
39
+ export function getPackedEntityType(itemId: number): Name | null {
40
+ switch (itemId) {
41
+ case ITEM_SHIP_T1_PACKED:
42
+ return ENTITY_SHIP
43
+ case ITEM_CONTAINER_T1_PACKED:
44
+ case ITEM_CONTAINER_T2_PACKED:
45
+ return ENTITY_CONTAINER
46
+ case ITEM_WAREHOUSE_T1_PACKED:
47
+ return ENTITY_WAREHOUSE
48
+ case ITEM_EXTRACTOR_T1_PACKED:
49
+ return ENTITY_EXTRACTOR
50
+ case ITEM_FACTORY_T1_PACKED:
51
+ return ENTITY_FACTORY
52
+ default:
53
+ return null
54
+ }
55
+ }
56
+
57
+ export type EntityTypeName = 'ship' | 'warehouse' | 'extractor' | 'factory' | 'container' | 'nexus'
8
58
 
9
59
  export interface EntityTraits {
10
60
  typeName: Name
11
61
  isMovable: boolean
12
62
  hasEnergy: boolean
13
63
  hasLoaders: boolean
64
+ hasModules: boolean
14
65
  notFoundError: string
15
66
  }
16
67
 
@@ -19,6 +70,7 @@ export const shipTraits: EntityTraits = {
19
70
  isMovable: true,
20
71
  hasEnergy: true,
21
72
  hasLoaders: true,
73
+ hasModules: true,
22
74
 
23
75
  notFoundError: 'ship not found',
24
76
  }
@@ -28,19 +80,51 @@ export const warehouseTraits: EntityTraits = {
28
80
  isMovable: false,
29
81
  hasEnergy: false,
30
82
  hasLoaders: true,
83
+ hasModules: true,
31
84
 
32
85
  notFoundError: 'warehouse not found',
33
86
  }
34
87
 
88
+ export const extractorTraits: EntityTraits = {
89
+ typeName: ENTITY_EXTRACTOR,
90
+ isMovable: false,
91
+ hasEnergy: true,
92
+ hasLoaders: false,
93
+ hasModules: true,
94
+
95
+ notFoundError: 'extractor not found',
96
+ }
97
+
98
+ export const factoryTraits: EntityTraits = {
99
+ typeName: ENTITY_FACTORY,
100
+ isMovable: false,
101
+ hasEnergy: true,
102
+ hasLoaders: false,
103
+ hasModules: true,
104
+
105
+ notFoundError: 'factory not found',
106
+ }
107
+
35
108
  export const containerTraits: EntityTraits = {
36
109
  typeName: ENTITY_CONTAINER,
37
110
  isMovable: true,
38
111
  hasEnergy: false,
39
112
  hasLoaders: false,
113
+ hasModules: false,
40
114
 
41
115
  notFoundError: 'container not found',
42
116
  }
43
117
 
118
+ export const nexusTraits: EntityTraits = {
119
+ typeName: ENTITY_NEXUS,
120
+ isMovable: false,
121
+ hasEnergy: false,
122
+ hasLoaders: false,
123
+ hasModules: false,
124
+
125
+ notFoundError: 'nexus not found',
126
+ }
127
+
44
128
  export function getEntityTraits(entityType: Name | EntityTypeName): EntityTraits {
45
129
  const typeName = typeof entityType === 'string' ? entityType : entityType.toString()
46
130
 
@@ -49,8 +133,14 @@ export function getEntityTraits(entityType: Name | EntityTypeName): EntityTraits
49
133
  return shipTraits
50
134
  case 'warehouse':
51
135
  return warehouseTraits
136
+ case 'extractor':
137
+ return extractorTraits
138
+ case 'factory':
139
+ return factoryTraits
52
140
  case 'container':
53
141
  return containerTraits
142
+ case 'nexus':
143
+ return nexusTraits
54
144
  default:
55
145
  throw new Error(`Unknown entity type: ${typeName}`)
56
146
  }
@@ -64,6 +154,18 @@ export function isWarehouse(entity: {type?: Name}): boolean {
64
154
  return entity.type?.equals(ENTITY_WAREHOUSE) ?? false
65
155
  }
66
156
 
157
+ export function isExtractor(entity: {type?: Name}): boolean {
158
+ return entity.type?.equals(ENTITY_EXTRACTOR) ?? false
159
+ }
160
+
161
+ export function isFactory(entity: {type?: Name}): boolean {
162
+ return entity.type?.equals(ENTITY_FACTORY) ?? false
163
+ }
164
+
67
165
  export function isContainer(entity: {type?: Name}): boolean {
68
166
  return entity.type?.equals(ENTITY_CONTAINER) ?? false
69
167
  }
168
+
169
+ export function isNexus(entity: {type?: Name}): boolean {
170
+ return entity.type?.equals(ENTITY_NEXUS) ?? false
171
+ }
package/src/types.ts CHANGED
@@ -12,7 +12,7 @@ import {ServerContract} from './contracts'
12
12
  export const PRECISION = 10000
13
13
  export const CRAFT_ENERGY_DIVISOR = 150000
14
14
 
15
- export const WAREHOUSE_Z = 500
15
+ export const PLANETARY_STRUCTURE_Z = 0
16
16
 
17
17
  export const CONTAINER_Z = 300
18
18
 
@@ -23,6 +23,8 @@ export const MAX_ORBITAL_ALTITUDE = 3000
23
23
 
24
24
  export const BASE_ORBITAL_MASS = 100000
25
25
 
26
+ export const MIN_TRANSFER_DISTANCE = 100
27
+
26
28
  export interface ShipLike {
27
29
  coordinates: ServerContract.Types.coordinates
28
30
  hullmass?: UInt32
@@ -51,6 +53,9 @@ export enum TaskType {
51
53
  DEPLOY = 8,
52
54
  WRAP = 9,
53
55
  UNWRAP = 10,
56
+ UNDEPLOY = 11,
57
+ WRAP_ENTITY = 12,
58
+ DEMOLISH = 13,
54
59
  }
55
60
 
56
61
  export enum LocationType {
@@ -58,6 +63,7 @@ export enum LocationType {
58
63
  PLANET = 1,
59
64
  ASTEROID = 2,
60
65
  NEBULA = 3,
66
+ ICE_FIELD = 4,
61
67
  }
62
68
 
63
69
  export enum TaskCancelable {
@@ -159,3 +165,7 @@ export interface Item {
159
165
  export function formatTier(tier: number): string {
160
166
  return 'T' + tier
161
167
  }
168
+
169
+ export function tierAdjective(tier: number): string {
170
+ return TIER_ADJECTIVES[tier] ?? `T${tier}`
171
+ }
@@ -0,0 +1,27 @@
1
+ import type {ServerContract} from '../contracts'
2
+
3
+ export function cargoRef(src: {
4
+ item_id: number
5
+ stats: bigint | number
6
+ modules?: ServerContract.Types.module_entry[]
7
+ }): ServerContract.ActionParams.Type.cargo_ref {
8
+ return {
9
+ item_id: src.item_id,
10
+ stats: src.stats,
11
+ modules: src.modules ?? [],
12
+ }
13
+ }
14
+
15
+ export function cargoItem(
16
+ src: {
17
+ item_id: number
18
+ stats: bigint | number
19
+ modules?: ServerContract.Types.module_entry[]
20
+ },
21
+ quantity: bigint | number
22
+ ): ServerContract.ActionParams.Type.cargo_item {
23
+ return {
24
+ ...cargoRef(src),
25
+ quantity,
26
+ }
27
+ }