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

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.
@@ -155,7 +155,8 @@ function resolveComponent(id: number, stats?: UInt64Type): ResolvedItem {
155
155
 
156
156
  function computeCapabilityGroup(
157
157
  moduleType: number,
158
- stats: Record<string, number>
158
+ stats: Record<string, number>,
159
+ tier: number
159
160
  ): ResolvedAttributeGroup | undefined {
160
161
  switch (moduleType) {
161
162
  case MODULE_ENGINE: {
@@ -179,7 +180,7 @@ function computeCapabilityGroup(
179
180
  }
180
181
  }
181
182
  case MODULE_GATHERER: {
182
- const caps = computeGathererCapabilities(stats)
183
+ const caps = computeGathererCapabilities(stats, tier)
183
184
  return {
184
185
  capability: 'Gatherer',
185
186
  attributes: [
@@ -242,7 +243,7 @@ function resolveModule(id: number, stats?: UInt64Type): ResolvedItem {
242
243
  if (stats !== undefined) {
243
244
  const decoded = decodeCraftedItemStats(id, toBigStats(stats))
244
245
  const modType = getModuleCapabilityType(id)
245
- const group = computeCapabilityGroup(modType, decoded)
246
+ const group = computeCapabilityGroup(modType, decoded, item.tier)
246
247
  if (group) attributes = [group]
247
248
  }
248
249
  return {
@@ -311,13 +312,16 @@ function resolveEntity(
311
312
  const modStats = BigInt(mod.installed.stats.toString())
312
313
  const decodedStats = decodeCraftedItemStats(modItemId, modStats)
313
314
  const modType = getModuleCapabilityType(modItemId)
314
- const group = computeCapabilityGroup(modType, decodedStats)
315
315
  let modName = 'Module'
316
+ let modTier = 1
316
317
  try {
317
- modName = getItem(modItemId).name
318
+ const modItem = getItem(modItemId)
319
+ modName = modItem.name
320
+ modTier = modItem.tier
318
321
  } catch {
319
322
  modName = itemMetadata[modItemId]?.name ?? 'Module'
320
323
  }
324
+ const group = computeCapabilityGroup(modType, decodedStats, modTier)
321
325
  return {
322
326
  name: modName,
323
327
  installed: true,
@@ -72,6 +72,10 @@ export class ScheduleAccessor {
72
72
  return schedule.currentTaskProgress(this.entity, now)
73
73
  }
74
74
 
75
+ currentTaskProgressFloat(now: Date): number {
76
+ return schedule.currentTaskProgressFloat(this.entity, now)
77
+ }
78
+
75
79
  progress(now: Date): number {
76
80
  return schedule.scheduleProgress(this.entity, now)
77
81
  }
@@ -267,6 +267,10 @@ function applyTask(projected: ProjectedEntity, task: ServerContract.Types.task):
267
267
  case TaskType.DEPLOY:
268
268
  applyDeployTask(projected, task)
269
269
  break
270
+ case TaskType.UNDEPLOY:
271
+ case TaskType.WRAP_ENTITY:
272
+ case TaskType.DEMOLISH:
273
+ break
270
274
  }
271
275
  }
272
276
 
@@ -448,6 +452,10 @@ export function projectEntityAt(entity: Projectable, now: Date): ProjectedEntity
448
452
  case TaskType.DEPLOY:
449
453
  if (taskComplete) applyDeployTask(projected, task)
450
454
  break
455
+ case TaskType.UNDEPLOY:
456
+ case TaskType.WRAP_ENTITY:
457
+ case TaskType.DEMOLISH:
458
+ break
451
459
  }
452
460
  }
453
461
 
@@ -77,7 +77,7 @@ export function currentTaskIndex(entity: ScheduleData, now: Date): number {
77
77
  timeAccum += taskDuration
78
78
  }
79
79
 
80
- return entity.schedule.tasks.length - 1
80
+ return -1
81
81
  }
82
82
 
83
83
  export function currentTask(entity: ScheduleData, now: Date): Task | undefined {
@@ -147,6 +147,20 @@ export function currentTaskProgress(entity: ScheduleData, now: Date): number {
147
147
  return Math.min(1, elapsed / duration)
148
148
  }
149
149
 
150
+ export function currentTaskProgressFloat(entity: ScheduleData, now: Date): number {
151
+ if (!entity.schedule || entity.schedule.tasks.length === 0) return 0
152
+ const index = currentTaskIndex(entity, now)
153
+ if (index < 0) return 0
154
+ const task = entity.schedule.tasks[index]
155
+ const durationMs = task.duration.toNumber() * 1000
156
+ if (durationMs === 0) return 1
157
+ const startedMs = entity.schedule.started.toDate().getTime()
158
+ const taskStartMs = startedMs + getTaskStartTime(entity, index) * 1000
159
+ const elapsedMs = now.getTime() - taskStartMs
160
+ if (elapsedMs <= 0) return 0
161
+ return Math.min(1, elapsedMs / durationMs)
162
+ }
163
+
150
164
  export function scheduleProgress(entity: ScheduleData, now: Date): number {
151
165
  const duration = scheduleDuration(entity)
152
166
  if (duration === 0) return hasSchedule(entity) ? 1 : 0
@@ -34,6 +34,12 @@ export interface BoundsSubscriptionHandle {
34
34
  current: Map<number, EntityInstance>
35
35
  }
36
36
 
37
+ export interface OwnerSubscriptionHandle {
38
+ readonly subId: string
39
+ unsubscribe(): void
40
+ current: Map<number, EntityInstance>
41
+ }
42
+
37
43
  export interface EntitySubscriptionHandle {
38
44
  readonly subId: string
39
45
  readonly entityType: SubscriptionEntityType
@@ -62,7 +68,7 @@ export class SubscriptionsManager {
62
68
  onSnapshot?: (entities: EntityInstance[]) => void
63
69
  onUpdate?: (entity: EntityInstance) => void
64
70
  onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
65
- handle: BoundsSubscriptionHandle
71
+ handle: BoundsSubscriptionHandle | OwnerSubscriptionHandle
66
72
  }
67
73
  >()
68
74
  private subCounter = 0
@@ -162,6 +168,36 @@ export class SubscriptionsManager {
162
168
  return handle
163
169
  }
164
170
 
171
+ subscribeOwner(
172
+ owner: string,
173
+ handlers: {
174
+ onSnapshot?: (entities: EntityInstance[]) => void
175
+ onUpdate?: (entity: EntityInstance) => void
176
+ } = {}
177
+ ): OwnerSubscriptionHandle {
178
+ const subId = this.generateSubID('own')
179
+ const msg: SubscribeMessage = {
180
+ type: 'subscribe',
181
+ sub_id: subId,
182
+ owner,
183
+ }
184
+ const handle: OwnerSubscriptionHandle = {
185
+ subId,
186
+ unsubscribe: () => this.unsubscribeBounds(subId),
187
+ current: new Map(),
188
+ }
189
+ this.boundsSubs.set(subId, {
190
+ bounds: undefined,
191
+ owner,
192
+ prioritizeOwner: undefined,
193
+ onSnapshot: handlers.onSnapshot,
194
+ onUpdate: handlers.onUpdate,
195
+ handle,
196
+ })
197
+ this.sendMessage(msg)
198
+ return handle
199
+ }
200
+
165
201
  private unsubscribeBounds(subId: string) {
166
202
  this.boundsSubs.delete(subId)
167
203
  this.sendMessage({type: 'unsubscribe', sub_id: subId})
@@ -77,6 +77,59 @@ export function lerp(
77
77
  }
78
78
  }
79
79
 
80
+ export interface FloatPosition {
81
+ x: number
82
+ y: number
83
+ }
84
+
85
+ export function easeFlightProgress(t: number): number {
86
+ if (t <= 0) return 0
87
+ if (t >= 1) return 1
88
+ return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t)
89
+ }
90
+
91
+ export function flightSpeedFactor(t: number): number {
92
+ if (t <= 0 || t >= 1) return 0
93
+ return t < 0.5 ? 4 * t : 4 * (1 - t)
94
+ }
95
+
96
+ export function interpolateFlightPosition(
97
+ origin: {x: Int64Type | number; y: Int64Type | number},
98
+ destination: {x: Int64Type | number; y: Int64Type | number},
99
+ taskProgress: number,
100
+ options?: {easing?: 'physics' | 'linear'}
101
+ ): FloatPosition {
102
+ const t = options?.easing === 'linear' ? taskProgress : easeFlightProgress(taskProgress)
103
+ return {
104
+ x: (1 - t) * Number(origin.x) + t * Number(destination.x),
105
+ y: (1 - t) * Number(origin.y) + t * Number(destination.y),
106
+ }
107
+ }
108
+
109
+ export function getInterpolatedPosition(
110
+ entity: HasScheduleAndLocation,
111
+ taskIndex: number,
112
+ taskProgress: number
113
+ ): FloatPosition {
114
+ if (!entity.schedule || entity.schedule.tasks.length === 0) {
115
+ return {x: Number(entity.coordinates.x), y: Number(entity.coordinates.y)}
116
+ }
117
+ if (taskIndex < 0) {
118
+ const settled = getFlightOrigin(entity, entity.schedule.tasks.length)
119
+ return {x: Number(settled.x), y: Number(settled.y)}
120
+ }
121
+ const task = entity.schedule.tasks[taskIndex]
122
+ if (!task.type.equals(TaskType.TRAVEL) || !task.coordinates) {
123
+ const origin = getFlightOrigin(entity, taskIndex)
124
+ return {x: Number(origin.x), y: Number(origin.y)}
125
+ }
126
+ return interpolateFlightPosition(
127
+ getFlightOrigin(entity, taskIndex),
128
+ task.coordinates,
129
+ taskProgress
130
+ )
131
+ }
132
+
80
133
  export function rotation(
81
134
  origin: ServerContract.ActionParams.Type.coordinates,
82
135
  destination: ServerContract.ActionParams.Type.coordinates
@@ -408,14 +461,18 @@ export function getDestinationLocation(
408
461
  return undefined
409
462
  }
410
463
 
464
+ /** Returns chain-tile coordinates (rounded). For visual position use getInterpolatedPosition. */
411
465
  export function getPositionAt(
412
466
  entity: HasScheduleAndLocation,
413
467
  taskIndex: number,
414
468
  taskProgress: number
415
469
  ): ServerContract.ActionParams.Type.coordinates {
416
- if (!entity.schedule || entity.schedule.tasks.length === 0 || taskIndex < 0) {
470
+ if (!entity.schedule || entity.schedule.tasks.length === 0) {
417
471
  return entity.coordinates
418
472
  }
473
+ if (taskIndex < 0) {
474
+ return getFlightOrigin(entity, entity.schedule.tasks.length)
475
+ }
419
476
 
420
477
  const task = entity.schedule.tasks[taskIndex]
421
478
 
package/src/types.ts CHANGED
@@ -51,6 +51,9 @@ export enum TaskType {
51
51
  DEPLOY = 8,
52
52
  WRAP = 9,
53
53
  UNWRAP = 10,
54
+ UNDEPLOY = 11,
55
+ WRAP_ENTITY = 12,
56
+ DEMOLISH = 13,
54
57
  }
55
58
 
56
59
  export enum LocationType {
@@ -58,6 +61,7 @@ export enum LocationType {
58
61
  PLANET = 1,
59
62
  ASTEROID = 2,
60
63
  NEBULA = 3,
64
+ ICE_FIELD = 4,
61
65
  }
62
66
 
63
67
  export enum TaskCancelable {
@@ -22,12 +22,10 @@ export function getLocationType(
22
22
  return LocationType.EMPTY
23
23
  }
24
24
 
25
- if (hashResult.array[1] < 96) {
26
- return LocationType.PLANET
27
- } else if (hashResult.array[1] < 176) {
28
- return LocationType.ASTEROID
29
- }
30
- return LocationType.NEBULA
25
+ if (hashResult.array[1] < 96) return LocationType.PLANET
26
+ if (hashResult.array[1] < 149) return LocationType.ASTEROID
27
+ if (hashResult.array[1] < 202) return LocationType.NEBULA
28
+ return LocationType.ICE_FIELD
31
29
  }
32
30
 
33
31
  export function isGatherableLocation(locationType: LocationType): boolean {
@@ -44,6 +42,8 @@ export function getLocationTypeName(type: LocationType): string {
44
42
  return 'Asteroid'
45
43
  case LocationType.NEBULA:
46
44
  return 'Nebula'
45
+ case LocationType.ICE_FIELD:
46
+ return 'Ice Field'
47
47
  }
48
48
  }
49
49
 
@@ -76,6 +76,15 @@ function generateNebulaName(hashResult: Checksum512): string {
76
76
  return `${nebulaAdjectives[adjIdx]} ${nebulaNouns[nounIdx]}`
77
77
  }
78
78
 
79
+ function generateIceFieldName(hashResult: Checksum512): string {
80
+ const A = 65
81
+ const letter1 = String.fromCharCode(A + (hashResult.array[0] % 26))
82
+ const letter2 = String.fromCharCode(A + (hashResult.array[1] % 26))
83
+ const subId = (hashResult.array[2] % 9) + 1
84
+ const num = (uint16(hashResult, 3) % 9000) + 1000
85
+ return `${letter1}${letter2}-${subId}/${num}`
86
+ }
87
+
79
88
  export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesType): string {
80
89
  const seed = Checksum256.from(gameSeed)
81
90
  const locationType = getLocationType(seed, location)
@@ -91,6 +100,8 @@ export function getSystemName(gameSeed: Checksum256Type, location: CoordinatesTy
91
100
  return generateAsteroidName(hashResult)
92
101
  case LocationType.NEBULA:
93
102
  return generateNebulaName(hashResult)
103
+ case LocationType.ICE_FIELD:
104
+ return generateIceFieldName(hashResult)
94
105
  default:
95
106
  return generatePlanetName(hashResult)
96
107
  }
@@ -123,10 +134,12 @@ export function deriveLocationStatic(
123
134
 
124
135
  if (hashResult.array[1] < 96) {
125
136
  loc.type = UInt8.from(LocationType.PLANET)
126
- } else if (hashResult.array[1] < 176) {
137
+ } else if (hashResult.array[1] < 149) {
127
138
  loc.type = UInt8.from(LocationType.ASTEROID)
128
- } else {
139
+ } else if (hashResult.array[1] < 202) {
129
140
  loc.type = UInt8.from(LocationType.NEBULA)
141
+ } else {
142
+ loc.type = UInt8.from(LocationType.ICE_FIELD)
130
143
  }
131
144
 
132
145
  loc.subtype = UInt8.from(