@shipload/sdk 1.0.0-next.7 → 1.0.0-next.71

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 (203) 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 +5822 -1484
  7. package/lib/shipload.js +20838 -5419
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +20674 -5669
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1691 -0
  12. package/lib/testing.js +7109 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +7103 -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 +80 -0
  20. package/src/capabilities/crafting.ts +50 -8
  21. package/src/capabilities/gathering.test.ts +29 -0
  22. package/src/capabilities/gathering.ts +38 -20
  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.test.ts +26 -0
  28. package/src/capabilities/storage.ts +22 -7
  29. package/src/contracts/fund.ts +306 -0
  30. package/src/contracts/platform.ts +312 -3
  31. package/src/contracts/server.ts +2793 -478
  32. package/src/contracts/token.ts +170 -0
  33. package/src/contracts.ts +2 -0
  34. package/src/coordinates/address.ts +88 -0
  35. package/src/coordinates/constants.test.ts +15 -0
  36. package/src/coordinates/constants.ts +23 -0
  37. package/src/coordinates/index.ts +15 -0
  38. package/src/coordinates/memo.test.ts +47 -0
  39. package/src/coordinates/memo.ts +20 -0
  40. package/src/coordinates/permutation.ts +77 -0
  41. package/src/coordinates/regions.ts +48 -0
  42. package/src/coordinates/sectors.ts +115 -0
  43. package/src/data/capabilities.ts +49 -18
  44. package/src/data/capability-formulas.ts +26 -14
  45. package/src/data/catalog.test.ts +19 -0
  46. package/src/data/catalog.ts +13 -3
  47. package/src/data/charters.json +396 -0
  48. package/src/data/citizenry-adjectives.json +42 -0
  49. package/src/data/citizenry-nouns.json +50 -0
  50. package/src/data/colors.ts +14 -32
  51. package/src/data/entities.json +421 -15
  52. package/src/data/item-ids.ts +53 -13
  53. package/src/data/items.json +330 -77
  54. package/src/data/kind-registry.json +212 -0
  55. package/src/data/kind-registry.ts +192 -0
  56. package/src/data/metadata.ts +312 -51
  57. package/src/data/recipes-runtime.ts +6 -23
  58. package/src/data/recipes.json +2173 -173
  59. package/src/data/tiers.ts +9 -6
  60. package/src/derivation/build-methods.test.ts +71 -0
  61. package/src/derivation/build-methods.ts +50 -0
  62. package/src/derivation/capabilities.test.ts +348 -0
  63. package/src/derivation/capabilities.ts +707 -0
  64. package/src/derivation/capability-mappings.ts +60 -16
  65. package/src/derivation/crafting.test.ts +17 -0
  66. package/src/derivation/crafting.ts +57 -34
  67. package/src/derivation/index.ts +27 -2
  68. package/src/derivation/recipe-usage.test.ts +88 -0
  69. package/src/derivation/recipe-usage.ts +141 -0
  70. package/src/derivation/reserve-regen.ts +34 -0
  71. package/src/derivation/resources.ts +9 -1
  72. package/src/derivation/rollups.test.ts +55 -0
  73. package/src/derivation/rollups.ts +72 -0
  74. package/src/derivation/stars.test.ts +67 -0
  75. package/src/derivation/stars.ts +25 -0
  76. package/src/derivation/stat-scaling.ts +12 -0
  77. package/src/derivation/stats.ts +6 -6
  78. package/src/derivation/strata.ts +5 -3
  79. package/src/derivation/stratum.ts +26 -22
  80. package/src/derivation/tiers.ts +40 -7
  81. package/src/derivation/upgrades.ts +14 -0
  82. package/src/derivation/wormhole.ts +154 -0
  83. package/src/entities/entity.ts +102 -0
  84. package/src/entities/gamestate.ts +3 -28
  85. package/src/entities/makers.ts +144 -136
  86. package/src/entities/slot-multiplier.ts +43 -0
  87. package/src/entity/hydrate.test.ts +156 -0
  88. package/src/entity/hydrate.ts +86 -0
  89. package/src/errors.ts +21 -16
  90. package/src/format.ts +42 -9
  91. package/src/index-module.ts +425 -32
  92. package/src/influence/ballot.test.ts +103 -0
  93. package/src/influence/ballot.ts +98 -0
  94. package/src/influence/categories.ts +32 -0
  95. package/src/influence/charter-metadata.test.ts +136 -0
  96. package/src/influence/charter-metadata.ts +236 -0
  97. package/src/influence/charters.test.ts +112 -0
  98. package/src/influence/charters.ts +150 -0
  99. package/src/influence/citizenry.ts +38 -0
  100. package/src/influence/civic-shuttle.test.ts +202 -0
  101. package/src/influence/civic-shuttle.ts +142 -0
  102. package/src/influence/civic.test.ts +287 -0
  103. package/src/influence/civic.ts +269 -0
  104. package/src/influence/constants.ts +84 -0
  105. package/src/influence/decay.ts +20 -0
  106. package/src/influence/decomp.ts +95 -0
  107. package/src/influence/demand.ts +106 -0
  108. package/src/influence/duration.ts +101 -0
  109. package/src/influence/index.ts +118 -0
  110. package/src/influence/influence.test.ts +450 -0
  111. package/src/influence/quality.ts +25 -0
  112. package/src/influence/valuation.ts +154 -0
  113. package/src/managers/actions.ts +950 -93
  114. package/src/managers/balances.test.ts +161 -0
  115. package/src/managers/balances.ts +93 -0
  116. package/src/managers/base.ts +10 -2
  117. package/src/managers/cluster.test.ts +39 -0
  118. package/src/managers/cluster.ts +53 -0
  119. package/src/managers/construction-types.ts +80 -0
  120. package/src/managers/construction.ts +453 -0
  121. package/src/managers/context.ts +58 -1
  122. package/src/managers/coordinates.ts +14 -0
  123. package/src/managers/entities.ts +35 -63
  124. package/src/managers/epochs.ts +72 -1
  125. package/src/managers/index.ts +38 -1
  126. package/src/managers/influence.test.ts +56 -0
  127. package/src/managers/influence.ts +425 -0
  128. package/src/managers/jobs.test.ts +96 -0
  129. package/src/managers/jobs.ts +58 -0
  130. package/src/managers/locations.ts +25 -29
  131. package/src/managers/nft.test.ts +21 -0
  132. package/src/managers/nft.ts +110 -0
  133. package/src/managers/players.ts +25 -0
  134. package/src/managers/plot.ts +122 -0
  135. package/src/nft/atomicassets.abi.json +1342 -0
  136. package/src/nft/atomicassets.ts +237 -0
  137. package/src/nft/atomicdata.ts +130 -0
  138. package/src/nft/buildImmutableData.ts +388 -0
  139. package/src/nft/description.ts +222 -100
  140. package/src/nft/index.ts +3 -0
  141. package/src/planner/index.ts +296 -0
  142. package/src/planner/planner.test.ts +459 -0
  143. package/src/receipts.ts +22 -0
  144. package/src/resolution/describe-module.ts +43 -25
  145. package/src/resolution/display-name.ts +39 -11
  146. package/src/resolution/resolve-item.test.ts +48 -0
  147. package/src/resolution/resolve-item.ts +112 -58
  148. package/src/scan/index.ts +244 -0
  149. package/src/scan/scan-wasm.base64.ts +2 -0
  150. package/src/scheduling/accessor.ts +65 -23
  151. package/src/scheduling/availability.test.ts +320 -0
  152. package/src/scheduling/availability.ts +251 -0
  153. package/src/scheduling/cancel.test.ts +550 -0
  154. package/src/scheduling/cancel.ts +254 -0
  155. package/src/scheduling/cluster-stock.test.ts +31 -0
  156. package/src/scheduling/cluster-stock.ts +15 -0
  157. package/src/scheduling/energy.ts +47 -0
  158. package/src/scheduling/epoch.ts +1 -0
  159. package/src/scheduling/idle-resolve.ts +45 -0
  160. package/src/scheduling/jobs.test.ts +448 -0
  161. package/src/scheduling/jobs.ts +343 -0
  162. package/src/scheduling/lane-core.ts +128 -0
  163. package/src/scheduling/lanes.test.ts +250 -0
  164. package/src/scheduling/lanes.ts +279 -0
  165. package/src/scheduling/projection.ts +265 -149
  166. package/src/scheduling/schedule.test.ts +163 -0
  167. package/src/scheduling/schedule.ts +282 -116
  168. package/src/scheduling/task-cargo.test.ts +110 -0
  169. package/src/scheduling/task-cargo.ts +62 -0
  170. package/src/scheduling/task-effects.test.ts +35 -0
  171. package/src/scheduling/task-effects.ts +34 -0
  172. package/src/scheduling/unwrap.test.ts +86 -0
  173. package/src/scheduling/unwrap.ts +190 -0
  174. package/src/shipload.ts +63 -4
  175. package/src/subscriptions/manager.cluster.test.ts +51 -0
  176. package/src/subscriptions/manager.ts +245 -172
  177. package/src/subscriptions/mappers.ts +5 -8
  178. package/src/subscriptions/types.ts +28 -3
  179. package/src/testing/catalog-hash.ts +19 -0
  180. package/src/testing/index.ts +2 -0
  181. package/src/testing/projection-parity.ts +167 -0
  182. package/src/travel/reach.test.ts +86 -0
  183. package/src/travel/reach.ts +34 -0
  184. package/src/travel/route-build.test.ts +63 -0
  185. package/src/travel/route-build.ts +60 -0
  186. package/src/travel/route-planner.ts +288 -0
  187. package/src/travel/route-simulator.ts +170 -0
  188. package/src/travel/travel.ts +190 -123
  189. package/src/types/capabilities.ts +37 -8
  190. package/src/types/entity.ts +3 -3
  191. package/src/types/index.ts +0 -1
  192. package/src/types.ts +81 -23
  193. package/src/utils/cargo.test.ts +14 -0
  194. package/src/utils/cargo.ts +2 -0
  195. package/src/utils/display-name.ts +70 -0
  196. package/src/utils/hash.ts +22 -2
  197. package/src/utils/system.ts +24 -5
  198. package/src/capabilities/loading.ts +0 -8
  199. package/src/entities/container.ts +0 -123
  200. package/src/entities/ship-deploy.ts +0 -295
  201. package/src/entities/ship.ts +0 -221
  202. package/src/entities/warehouse.ts +0 -127
  203. package/src/types/entity-traits.ts +0 -69
@@ -1,8 +1,7 @@
1
- import {Name, TimePoint, UInt16, UInt32, UInt64} from '@wharfkit/antelope'
1
+ import {Name, type TimePoint, type UInt16, UInt32, UInt64} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
- import {Coordinates, PRECISION, TaskType} from '../types'
3
+ import {Coordinates, TaskType} from '../types'
4
4
  import {
5
- capsHasLoaders,
6
5
  capsHasMovement,
7
6
  capsHasStorage,
8
7
  type EntityCapabilities,
@@ -14,38 +13,48 @@ import {
14
13
  RECIPE_INPUTS_INSUFFICIENT,
15
14
  RECIPE_INPUTS_INVALID,
16
15
  RECIPE_NOT_FOUND,
17
- SHIP_CARGO_NOT_LOADED,
16
+ ENTITY_CARGO_NOT_LOADED,
18
17
  } from '../errors'
19
- import {getRecipe, type RecipeInput} from '../data/recipes-runtime'
20
- import {getItem} from '../data/catalog'
21
- import {distanceBetweenCoordinates, lerp} from '../travel/travel'
18
+ import {getEntityLayout, getRecipe, type RecipeInput} from '../data/recipes-runtime'
19
+ import {ITEM_SHIP_T1_PACKED} from '../data/item-ids'
20
+ import {computeEntityCapabilities} from '../derivation/capabilities'
21
+ import {decodeCraftedItemStats, decodeStat} from '../derivation/crafting'
22
+ import {packedModulesToInstalled, type InstalledModule} from '../entities/slot-multiplier'
23
+ import {lerp} from '../travel/travel'
22
24
  import {
23
25
  calcStacksMass,
24
26
  cargoItemToStack,
25
27
  type CargoStack,
26
28
  mergeStacks,
27
- removeFromStacks,
29
+ subtractFromStacks,
28
30
  stackToCargoItem,
29
31
  } from '../capabilities/storage'
32
+ import {hydrateEntityLanes} from '../entity/hydrate'
33
+ import {craftCargoOwnership} from './availability'
30
34
  import * as schedule from './schedule'
31
35
  import type {ScheduleData} from './schedule'
32
36
 
33
37
  export interface ProjectedEntity {
34
38
  location: Coordinates
35
- energy: UInt16
39
+ energy: UInt32
36
40
  cargo: CargoStack[]
37
41
  shipMass: UInt32
38
42
  capacity?: UInt64
39
43
  engines?: ServerContract.Types.movement_stats
40
- loaders?: ServerContract.Types.loader_stats
44
+ loaderLanes: ServerContract.Types.loader_lane[]
41
45
  generator?: ServerContract.Types.energy_stats
42
46
  hauler?: ServerContract.Types.hauler_stats
47
+ launcher?: ServerContract.Types.launcher_stats
43
48
  readonly cargoMass: UInt64
44
49
  readonly totalMass: UInt64
50
+ readonly gathererLanes: ServerContract.Types.gatherer_lane[]
51
+ readonly crafterLanes: ServerContract.Types.crafter_lane[]
52
+ readonly builderLanes: ServerContract.Types.builder_lane[]
45
53
 
46
54
  hasMovement(): boolean
47
55
  hasStorage(): boolean
48
56
  hasLoaders(): boolean
57
+ hasLauncher(): boolean
49
58
 
50
59
  capabilities(): EntityCapabilities
51
60
  state(): EntityState
@@ -53,53 +62,196 @@ export interface ProjectedEntity {
53
62
 
54
63
  export interface Projectable extends ScheduleData {
55
64
  coordinates: Coordinates | ServerContract.Types.coordinates
56
- energy?: UInt16
65
+ energy?: UInt32
57
66
  hullmass?: UInt32
58
67
  generator?: ServerContract.Types.energy_stats
59
68
  engines?: ServerContract.Types.movement_stats
60
- loaders?: ServerContract.Types.loader_stats
69
+ loader_lanes?: ServerContract.Types.loader_lane[]
70
+ gatherer_lanes?: ServerContract.Types.gatherer_lane[]
71
+ crafter_lanes?: ServerContract.Types.crafter_lane[]
72
+ builder_lanes?: ServerContract.Types.builder_lane[]
61
73
  hauler?: ServerContract.Types.hauler_stats
74
+ launcher?: ServerContract.Types.launcher_stats
62
75
  capacity?: UInt32
63
76
  cargo: ServerContract.Types.cargo_item[]
64
77
  cargomass: UInt32
78
+ projected_at?: TimePoint
65
79
  owner?: Name
80
+ stats?: bigint
81
+ item_id?: number | UInt16
82
+ modules?: ServerContract.Types.module_entry[] | InstalledModule[]
66
83
  }
67
84
 
68
- function getHullMass(entity: Projectable): UInt32 {
69
- return UInt32.from(entity.hullmass ?? 0)
85
+ function toInstalledModules(
86
+ modules: ServerContract.Types.module_entry[] | InstalledModule[]
87
+ ): InstalledModule[] {
88
+ if (modules.length > 0 && 'itemId' in modules[0]) {
89
+ return modules as InstalledModule[]
90
+ }
91
+ return packedModulesToInstalled(modules as ServerContract.Types.module_entry[])
92
+ }
93
+
94
+ interface ProjectedCaps {
95
+ hullmass?: UInt32
96
+ capacity?: UInt32
97
+ engines?: ServerContract.Types.movement_stats
98
+ generator?: ServerContract.Types.energy_stats
99
+ loaderLanes: ServerContract.Types.loader_lane[]
100
+ gathererLanes: ServerContract.Types.gatherer_lane[]
101
+ crafterLanes: ServerContract.Types.crafter_lane[]
102
+ builderLanes: ServerContract.Types.builder_lane[]
103
+ hauler?: ServerContract.Types.hauler_stats
104
+ launcher?: ServerContract.Types.launcher_stats
105
+ }
106
+
107
+ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
108
+ if (
109
+ entity.item_id === undefined ||
110
+ entity.modules === undefined ||
111
+ entity.stats === undefined
112
+ ) {
113
+ return undefined
114
+ }
115
+
116
+ const itemId = Number(
117
+ typeof entity.item_id === 'number' ? entity.item_id : entity.item_id.value
118
+ )
119
+ const hullStats = decodeCraftedItemStats(itemId, entity.stats)
120
+ if (hullStats.strength === undefined) hullStats.strength = decodeStat(entity.stats, 0)
121
+ if (hullStats.hardness === undefined) hullStats.hardness = decodeStat(entity.stats, 2)
122
+ // The retired Ship T1 recipe only assigned names to its first two stat slots.
123
+ // Its on-chain entity row still carries five packed channels, and the ship hull
124
+ // formula deliberately consumes all five. Keep the unlabelled channels internal
125
+ // to projection so replay remains contract-parity until deployment replaces it.
126
+ if (itemId === ITEM_SHIP_T1_PACKED) {
127
+ hullStats.__legacyShipChannel3 = decodeStat(entity.stats, 3)
128
+ hullStats.__legacyShipChannel4 = decodeStat(entity.stats, 4)
129
+ }
130
+ const layout = getEntityLayout(itemId)?.slots ?? []
131
+ const installed = toInstalledModules(entity.modules)
132
+ const caps = computeEntityCapabilities(hullStats, itemId, installed, layout)
133
+
134
+ const toLoaderLane = (l: {
135
+ slotIndex: number
136
+ mass: number
137
+ thrust: number
138
+ outputPct: number
139
+ }): ServerContract.Types.loader_lane =>
140
+ ServerContract.Types.loader_lane.from({
141
+ slot_index: l.slotIndex,
142
+ mass: l.mass,
143
+ thrust: l.thrust,
144
+ output_pct: l.outputPct,
145
+ })
146
+
147
+ const toCrafterLane = (l: {
148
+ slotIndex: number
149
+ speed: number
150
+ drain: number
151
+ outputPct: number
152
+ }): ServerContract.Types.crafter_lane =>
153
+ ServerContract.Types.crafter_lane.from({
154
+ slot_index: l.slotIndex,
155
+ speed: l.speed,
156
+ drain: l.drain,
157
+ output_pct: l.outputPct,
158
+ })
159
+
160
+ const toBuilderLane = (l: {
161
+ slotIndex: number
162
+ speed: number
163
+ drain: number
164
+ outputPct: number
165
+ }): ServerContract.Types.builder_lane =>
166
+ ServerContract.Types.builder_lane.from({
167
+ slot_index: l.slotIndex,
168
+ speed: l.speed,
169
+ drain: l.drain,
170
+ output_pct: l.outputPct,
171
+ })
172
+
173
+ return {
174
+ hullmass: UInt32.from(caps.hullmass),
175
+ capacity: UInt32.from(caps.capacity),
176
+ engines: caps.engines ? ServerContract.Types.movement_stats.from(caps.engines) : undefined,
177
+ generator: caps.generator
178
+ ? ServerContract.Types.energy_stats.from(caps.generator)
179
+ : undefined,
180
+ loaderLanes: (caps.loaderLanes ?? []).map(toLoaderLane),
181
+ gathererLanes: hydrateEntityLanes(caps),
182
+ crafterLanes: (caps.crafterLanes ?? []).map(toCrafterLane),
183
+ builderLanes: (caps.builderLanes ?? []).map(toBuilderLane),
184
+ hauler: caps.hauler
185
+ ? ServerContract.Types.hauler_stats.from({
186
+ capacity: caps.hauler.capacity,
187
+ efficiency: caps.hauler.efficiency,
188
+ drain: caps.hauler.drain,
189
+ capacity_by_tier: caps.hauler.capacityByTier,
190
+ })
191
+ : undefined,
192
+ launcher: caps.launcher
193
+ ? ServerContract.Types.launcher_stats.from({
194
+ charge_rate: caps.launcher.chargeRate,
195
+ velocity: caps.launcher.velocity,
196
+ drain: caps.launcher.drain,
197
+ })
198
+ : undefined,
199
+ }
200
+ }
201
+
202
+ function loaderLanesTotalMass(lanes: ServerContract.Types.loader_lane[]): UInt64 {
203
+ let total = 0
204
+ for (const l of lanes) total += Number(l.mass)
205
+ return UInt64.from(total)
70
206
  }
71
207
 
72
208
  export function createProjectedEntity(entity: Projectable): ProjectedEntity {
73
- const shipMass = getHullMass(entity)
74
- const loaders = entity.loaders
75
- const engines = entity.engines
76
- const generator = entity.generator
77
- const hauler = entity.hauler
78
- const capacity = entity.capacity
209
+ const needsRecompute =
210
+ entity.hullmass === undefined ||
211
+ entity.loader_lanes === undefined ||
212
+ entity.engines === undefined ||
213
+ entity.generator === undefined ||
214
+ entity.hauler === undefined ||
215
+ entity.launcher === undefined ||
216
+ entity.capacity === undefined
217
+ const caps = needsRecompute ? recomputeCaps(entity) : undefined
218
+
219
+ const shipMass = UInt32.from(entity.hullmass ?? caps?.hullmass ?? 0)
220
+ const loaderLanes = entity.loader_lanes ?? caps?.loaderLanes ?? []
221
+ const gathererLanes = entity.gatherer_lanes ?? caps?.gathererLanes ?? []
222
+ const crafterLanes = entity.crafter_lanes ?? caps?.crafterLanes ?? []
223
+ const builderLanes = entity.builder_lanes ?? caps?.builderLanes ?? []
224
+ const engines = entity.engines ?? caps?.engines
225
+ const generator = entity.generator ?? caps?.generator
226
+ const hauler = entity.hauler ?? caps?.hauler
227
+ const launcher = entity.launcher ?? caps?.launcher
228
+ const capacity = entity.capacity ?? caps?.capacity
79
229
 
80
230
  const cargo: CargoStack[] = entity.cargo.map(cargoItemToStack)
81
231
 
82
232
  const projected: ProjectedEntity = {
83
233
  location: Coordinates.from(entity.coordinates),
84
- energy: UInt16.from(entity.energy ?? 0),
234
+ energy: UInt32.from(entity.energy ?? 0),
85
235
  cargo,
86
236
  shipMass,
87
237
  capacity: capacity ? UInt64.from(capacity) : undefined,
88
238
  engines,
89
239
  generator,
90
240
  hauler,
91
- loaders,
241
+ launcher,
242
+ loaderLanes,
243
+ gathererLanes,
244
+ crafterLanes,
245
+ builderLanes,
92
246
 
93
247
  get cargoMass() {
94
248
  return calcStacksMass(this.cargo)
95
249
  },
96
250
 
97
251
  get totalMass() {
98
- let mass = UInt64.from(this.shipMass).adding(this.cargoMass)
99
- if (this.loaders) {
100
- mass = mass.adding(this.loaders.mass.multiplying(this.loaders.quantity))
101
- }
102
- return mass
252
+ return UInt64.from(this.shipMass)
253
+ .adding(this.cargoMass)
254
+ .adding(loaderLanesTotalMass(this.loaderLanes))
103
255
  },
104
256
 
105
257
  hasMovement() {
@@ -111,7 +263,11 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
111
263
  },
112
264
 
113
265
  hasLoaders() {
114
- return capsHasLoaders(this.capabilities())
266
+ return this.loaderLanes.length > 0
267
+ },
268
+
269
+ hasLauncher() {
270
+ return this.launcher !== undefined
115
271
  },
116
272
 
117
273
  capabilities(): EntityCapabilities {
@@ -120,7 +276,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
120
276
  capacity: this.capacity ? UInt32.from(this.capacity) : undefined,
121
277
  engines: this.engines,
122
278
  generator: this.generator,
123
- loaders: this.loaders,
279
+ launcher: this.launcher,
124
280
  }
125
281
  },
126
282
 
@@ -146,12 +302,12 @@ function applyRechargeTask(
146
302
  if (!projected.generator) return
147
303
 
148
304
  if (options.complete) {
149
- projected.energy = UInt16.from(projected.generator.capacity)
305
+ projected.energy = UInt32.from(projected.generator.capacity)
150
306
  } else if (options.progress !== undefined) {
151
307
  const capacity = Number(projected.generator.capacity)
152
308
  const currentEnergy = Number(projected.energy)
153
309
  const rechargeAmount = (capacity - currentEnergy) * options.progress
154
- projected.energy = UInt16.from(Math.min(capacity, currentEnergy + rechargeAmount))
310
+ projected.energy = UInt32.from(Math.min(capacity, currentEnergy + rechargeAmount))
155
311
  }
156
312
  }
157
313
 
@@ -160,28 +316,25 @@ function applyFlightTask(
160
316
  task: ServerContract.Types.task,
161
317
  options: {complete: boolean; progress?: number}
162
318
  ): void {
163
- if (!task.coordinates || !projected.engines) return
319
+ if (!task.coordinates) return
164
320
 
165
- const origin = projected.location
166
321
  const destination = Coordinates.from(task.coordinates)
167
- const distance = distanceBetweenCoordinates(origin, task.coordinates)
168
- const energyUsage = distance.dividing(PRECISION).multiplying(projected.engines.drain)
169
322
 
170
323
  if (options.complete) {
171
- projected.energy = projected.energy.gt(energyUsage)
172
- ? UInt16.from(projected.energy.subtracting(energyUsage))
173
- : UInt16.from(0)
324
+ applyEnergyCost(projected, task)
174
325
  projected.location = destination
175
326
  } else if (options.progress !== undefined) {
176
- const interpolated = lerp(origin, destination, options.progress)
327
+ const interpolated = lerp(projected.location, destination, options.progress)
177
328
  projected.location = Coordinates.from({
178
329
  x: Math.round(interpolated.x),
179
330
  y: Math.round(interpolated.y),
180
331
  })
181
- const partialEnergy = UInt64.from(Math.floor(Number(energyUsage) * options.progress))
332
+ const partialEnergy = UInt64.from(
333
+ Math.floor(Number(task.energy_cost ?? 0) * options.progress)
334
+ )
182
335
  projected.energy = projected.energy.gt(partialEnergy)
183
- ? UInt16.from(projected.energy.subtracting(partialEnergy))
184
- : UInt16.from(0)
336
+ ? UInt32.from(projected.energy.subtracting(partialEnergy))
337
+ : UInt32.from(0)
185
338
  }
186
339
  }
187
340
 
@@ -190,7 +343,7 @@ function addCargoItem(projected: ProjectedEntity, item: ServerContract.Types.car
190
343
  }
191
344
 
192
345
  function removeCargoItem(projected: ProjectedEntity, item: ServerContract.Types.cargo_item): void {
193
- projected.cargo = removeFromStacks(projected.cargo, cargoItemToStack(item))
346
+ projected.cargo = subtractFromStacks(projected.cargo, cargoItemToStack(item))
194
347
  }
195
348
 
196
349
  function applyAddCargoTask(projected: ProjectedEntity, task: ServerContract.Types.task): void {
@@ -207,10 +360,10 @@ function applyRemoveCargoTask(projected: ProjectedEntity, task: ServerContract.T
207
360
 
208
361
  function applyEnergyCost(projected: ProjectedEntity, task: ServerContract.Types.task): void {
209
362
  if (!task.energy_cost) return
210
- const energyCost = UInt16.from(task.energy_cost)
363
+ const energyCost = UInt32.from(task.energy_cost)
211
364
  projected.energy = projected.energy.gt(energyCost)
212
- ? UInt16.from(projected.energy.subtracting(energyCost))
213
- : UInt16.from(0)
365
+ ? UInt32.from(projected.energy.subtracting(energyCost))
366
+ : UInt32.from(0)
214
367
  }
215
368
 
216
369
  function applyGatherTask(
@@ -220,7 +373,7 @@ function applyGatherTask(
220
373
  ): void {
221
374
  if (!options.complete) return
222
375
  applyEnergyCost(projected, task)
223
- if (!task.entitytarget) {
376
+ if (task.couplings.length === 0) {
224
377
  applyAddCargoTask(projected, task)
225
378
  }
226
379
  }
@@ -229,16 +382,14 @@ function applyCraftTask(projected: ProjectedEntity, task: ServerContract.Types.t
229
382
  applyEnergyCost(projected, task)
230
383
  if (task.cargo.length === 0) return
231
384
 
232
- for (let i = 0; i < task.cargo.length - 1; i++) {
233
- removeCargoItem(projected, task.cargo[i])
385
+ const {clustered, ownOutput} = craftCargoOwnership(task)
386
+ if (!clustered) {
387
+ for (let i = 0; i < task.cargo.length - 1; i++) {
388
+ removeCargoItem(projected, task.cargo[i])
389
+ }
234
390
  }
235
- addCargoItem(projected, task.cargo[task.cargo.length - 1])
236
- }
237
-
238
- function applyDeployTask(projected: ProjectedEntity, task: ServerContract.Types.task): void {
239
- applyEnergyCost(projected, task)
240
- if (task.cargo.length > 0) {
241
- removeCargoItem(projected, task.cargo[0])
391
+ if (ownOutput) {
392
+ addCargoItem(projected, task.cargo[task.cargo.length - 1])
242
393
  }
243
394
  }
244
395
 
@@ -248,14 +399,25 @@ function applyTask(projected: ProjectedEntity, task: ServerContract.Types.task):
248
399
  applyRechargeTask(projected, task, {complete: true})
249
400
  break
250
401
  case TaskType.TRAVEL:
402
+ case TaskType.WARP:
403
+ case TaskType.TRANSIT:
251
404
  applyFlightTask(projected, task, {complete: true})
252
405
  break
253
406
  case TaskType.LOAD:
254
407
  case TaskType.UNWRAP:
408
+ case TaskType.CIVIC_WITHDRAW:
255
409
  applyAddCargoTask(projected, task)
256
410
  break
257
411
  case TaskType.UNLOAD:
258
- case TaskType.WRAP:
412
+ case TaskType.CONTRIBUTE:
413
+ applyRemoveCargoTask(projected, task)
414
+ break
415
+ case TaskType.CIVIC_DEPOSIT:
416
+ applyEnergyCost(projected, task)
417
+ applyRemoveCargoTask(projected, task)
418
+ break
419
+ case TaskType.UPGRADE:
420
+ applyEnergyCost(projected, task)
259
421
  applyRemoveCargoTask(projected, task)
260
422
  break
261
423
  case TaskType.GATHER:
@@ -264,11 +426,9 @@ function applyTask(projected: ProjectedEntity, task: ServerContract.Types.task):
264
426
  case TaskType.CRAFT:
265
427
  applyCraftTask(projected, task)
266
428
  break
267
- case TaskType.DEPLOY:
268
- applyDeployTask(projected, task)
269
- break
270
429
  case TaskType.UNDEPLOY:
271
- case TaskType.WRAP_ENTITY:
430
+ applyAddCargoTask(projected, task)
431
+ break
272
432
  case TaskType.DEMOLISH:
273
433
  break
274
434
  }
@@ -278,52 +438,33 @@ export interface ProjectionOptions {
278
438
  upToTaskIndex?: number
279
439
  }
280
440
 
441
+ function anchoredSkipCount(entity: Projectable, ordered: readonly schedule.OrderedTask[]): number {
442
+ return schedule.appliedTaskCount(entity, ordered)
443
+ }
444
+
281
445
  export function projectEntity(entity: Projectable, options?: ProjectionOptions): ProjectedEntity {
282
446
  const projected = createProjectedEntity(entity)
283
- if (!entity.schedule || entity.schedule.tasks.length === 0) return projected
447
+ const ordered = schedule.orderedTasks(entity)
448
+ if (ordered.length === 0) return projected
284
449
 
285
- const tasks = entity.schedule.tasks
286
450
  const taskCount =
287
451
  options?.upToTaskIndex !== undefined
288
- ? Math.max(0, Math.min(options.upToTaskIndex, tasks.length))
289
- : tasks.length
452
+ ? Math.max(0, Math.min(options.upToTaskIndex, ordered.length))
453
+ : ordered.length
290
454
 
291
- for (let i = 0; i < taskCount; i++) {
292
- applyTask(projected, tasks[i])
455
+ for (let i = anchoredSkipCount(entity, ordered); i < taskCount; i++) {
456
+ applyTask(projected, ordered[i].task)
293
457
  }
294
458
  return projected
295
459
  }
296
460
 
297
- export interface ProjectableSnapshot extends Projectable {
298
- current_task?: ServerContract.Types.task
299
- pending_tasks?: ServerContract.Types.task[]
300
- }
301
-
302
- function buildRemainingProjectable(snapshot: ProjectableSnapshot): Projectable | null {
303
- if (!snapshot.schedule) return null
304
- const remainingTasks: ServerContract.Types.task[] = []
305
- if (snapshot.current_task) remainingTasks.push(snapshot.current_task)
306
- if (snapshot.pending_tasks?.length) remainingTasks.push(...snapshot.pending_tasks)
307
- if (remainingTasks.length === 0) return null
308
-
309
- const completedCount = snapshot.schedule.tasks.length - remainingTasks.length
310
- let startedMs = snapshot.schedule.started.toMilliseconds()
311
- for (let i = 0; i < completedCount; i++) {
312
- startedMs += snapshot.schedule.tasks[i].duration.toNumber() * 1000
313
- }
314
-
315
- return {
316
- ...snapshot,
317
- schedule: ServerContract.Types.schedule.from({
318
- started: TimePoint.fromMilliseconds(startedMs),
319
- tasks: remainingTasks,
320
- }),
461
+ export function projectRemainingAt(entity: Projectable, _now: Date): ProjectedEntity {
462
+ const projected = createProjectedEntity(entity)
463
+ const ordered = schedule.orderedTasks(entity)
464
+ for (let i = anchoredSkipCount(entity, ordered); i < ordered.length; i++) {
465
+ applyTask(projected, ordered[i].task)
321
466
  }
322
- }
323
-
324
- export function projectFromCurrentState(snapshot: ProjectableSnapshot): ProjectedEntity {
325
- const projectable = buildRemainingProjectable(snapshot)
326
- return projectable ? projectEntity(projectable) : createProjectedEntity(snapshot)
467
+ return projected
327
468
  }
328
469
 
329
470
  function getRecipeInputsForOutput(outputItemId: number): RecipeInput[] | undefined {
@@ -346,19 +487,10 @@ function validateCraftTask(task: ServerContract.Types.task, projected: Projected
346
487
  let matched = false
347
488
  for (let ri = 0; ri < recipe.length; ri++) {
348
489
  const req = recipe[ri]
349
- if ('itemId' in req) {
350
- if (input.item_id.toNumber() === req.itemId) {
351
- groupedInputs[ri].push(input)
352
- matched = true
353
- break
354
- }
355
- } else {
356
- const item = getItem(input.item_id)
357
- if (item.category === req.category && item.tier === req.tier) {
358
- groupedInputs[ri].push(input)
359
- matched = true
360
- break
361
- }
490
+ if (input.item_id.toNumber() === req.itemId) {
491
+ groupedInputs[ri].push(input)
492
+ matched = true
493
+ break
362
494
  }
363
495
  }
364
496
  if (!matched) throw new Error(RECIPE_INPUTS_INVALID)
@@ -389,15 +521,16 @@ function validateCraftTask(task: ServerContract.Types.task, projected: Projected
389
521
  break
390
522
  }
391
523
  }
392
- if (!found) throw new Error(SHIP_CARGO_NOT_LOADED)
524
+ if (!found) throw new Error(ENTITY_CARGO_NOT_LOADED)
393
525
  }
394
526
  }
395
527
 
396
528
  export function validateSchedule(entity: Projectable): void {
397
- if (!entity.schedule || entity.schedule.tasks.length === 0) return
529
+ const ordered = schedule.orderedTasks(entity)
530
+ if (ordered.length === 0) return
398
531
 
399
532
  const projected = createProjectedEntity(entity)
400
- for (const task of entity.schedule.tasks) {
533
+ for (const {task} of ordered) {
401
534
  if (task.type.toNumber() === TaskType.CRAFT) {
402
535
  validateCraftTask(task, projected)
403
536
  }
@@ -411,61 +544,44 @@ export function validateSchedule(entity: Projectable): void {
411
544
  export function projectEntityAt(entity: Projectable, now: Date): ProjectedEntity {
412
545
  const projected = createProjectedEntity(entity)
413
546
 
414
- if (!entity.schedule || entity.schedule.tasks.length === 0) {
547
+ const ordered = schedule.orderedTasks(entity)
548
+ if (ordered.length === 0) {
415
549
  return projected
416
550
  }
417
551
 
418
- for (let i = 0; i < entity.schedule.tasks.length; i++) {
419
- const task = entity.schedule.tasks[i]
420
- const taskComplete = schedule.isTaskComplete(entity, i, now)
421
- const taskInProgress = schedule.isTaskInProgress(entity, i, now)
552
+ const nowMs = now.getTime()
553
+ const skip = anchoredSkipCount(entity, ordered)
554
+
555
+ for (let i = skip; i < ordered.length; i++) {
556
+ const {task, startsAt} = ordered[i]
557
+ const duration = task.duration.toNumber()
558
+ const elapsed = Math.min(
559
+ Math.max(0, Math.floor((nowMs - startsAt.getTime()) / 1000)),
560
+ duration
561
+ )
562
+ const taskComplete = elapsed >= duration
563
+ const taskInProgress = elapsed > 0 && elapsed < duration
422
564
 
423
565
  if (!taskComplete && !taskInProgress) {
424
- break
566
+ continue
425
567
  }
426
568
 
427
- const progress = taskInProgress
428
- ? schedule.getTaskElapsed(entity, i, now) / task.duration.toNumber()
429
- : undefined
569
+ const progress = taskInProgress ? elapsed / duration : undefined
430
570
 
431
571
  switch (task.type.toNumber()) {
432
572
  case TaskType.RECHARGE:
433
573
  applyRechargeTask(projected, task, {complete: taskComplete, progress})
434
574
  break
435
575
  case TaskType.TRAVEL:
576
+ case TaskType.WARP:
577
+ case TaskType.TRANSIT:
436
578
  applyFlightTask(projected, task, {complete: taskComplete, progress})
437
579
  break
438
- case TaskType.LOAD:
439
- case TaskType.UNWRAP:
440
- if (taskComplete) applyAddCargoTask(projected, task)
441
- break
442
- case TaskType.UNLOAD:
443
- case TaskType.WRAP:
444
- if (taskComplete) applyRemoveCargoTask(projected, task)
445
- break
446
- case TaskType.GATHER:
447
- if (taskComplete) applyGatherTask(projected, task, {complete: true})
448
- break
449
- case TaskType.CRAFT:
450
- if (taskComplete) applyCraftTask(projected, task)
451
- break
452
- case TaskType.DEPLOY:
453
- if (taskComplete) applyDeployTask(projected, task)
454
- break
455
- case TaskType.UNDEPLOY:
456
- case TaskType.WRAP_ENTITY:
457
- case TaskType.DEMOLISH:
580
+ default:
581
+ if (taskComplete) applyTask(projected, task)
458
582
  break
459
583
  }
460
584
  }
461
585
 
462
586
  return projected
463
587
  }
464
-
465
- export function projectFromCurrentStateAt(
466
- snapshot: ProjectableSnapshot,
467
- now: Date
468
- ): ProjectedEntity {
469
- const projectable = buildRemainingProjectable(snapshot)
470
- return projectable ? projectEntityAt(projectable, now) : createProjectedEntity(snapshot)
471
- }